diff --git a/src/main/java/codechicken/nei/BookmarkCraftingChain.java b/src/main/java/codechicken/nei/BookmarkCraftingChain.java deleted file mode 100644 index 5ebd3fe1..00000000 --- a/src/main/java/codechicken/nei/BookmarkCraftingChain.java +++ /dev/null @@ -1,319 +0,0 @@ -package codechicken.nei; - -import java.util.ArrayList; -import java.util.HashMap; -import java.util.HashSet; -import java.util.List; - -import net.minecraft.item.ItemStack; -import net.minecraftforge.fluids.FluidStack; - -import codechicken.nei.BookmarkPanel.ItemStackMetadata; -import codechicken.nei.recipe.BookmarkRecipeId; -import codechicken.nei.recipe.StackInfo; - -public class BookmarkCraftingChain { - - public HashMap calculatedItems = new HashMap<>(); - public HashMap multiplier = new HashMap<>(); - - public HashMap inputs = new HashMap<>(); - public HashMap outputs = new HashMap<>(); - public HashMap remainder = new HashMap<>(); - public HashMap intermediate = new HashMap<>(); - - protected static class CraftingChainItem { - - public String guid; - public int stackIndex = 0; - public int recipeIndex = 0; - public int factor = 0; - public int count = 0; - public boolean ingredient = false; - public int fluidCellAmount = 1; - public ItemStack stack = null; - - public CraftingChainItem(ItemStack stack, ItemStackMetadata stackMetadata) { - FluidStack fluid = StackInfo.getFluid(stack); - this.recipeIndex = getRecipeIndex(stackMetadata.recipeId); - this.ingredient = stackMetadata.ingredient; - this.stack = stack; - - if (fluid != null) { - this.stackIndex = getStackIndex(fluid); - this.count = fluid.amount * Math.max(0, stack.stackSize); - this.fluidCellAmount = StackInfo.isFluidContainer(stack) ? fluid.amount : 1; - this.factor = this.fluidCellAmount * stackMetadata.factor; - } else { - this.stackIndex = getStackIndex(stack); - this.count = StackInfo.itemStackToNBT(stack).getInteger("Count"); - this.factor = stackMetadata.factor; - } - - this.guid = this.stackIndex + " " + this.recipeIndex; - } - - public ItemStack getItemStack(long count) { - return StackInfo.loadFromNBT( - StackInfo.itemStackToNBT(this.stack), - this.factor > 0 ? (count / this.fluidCellAmount) : 0); - } - - public static void clearStatic() { - itemCache.clear(); - fluidCache.clear(); - recipeCache.clear(); - } - - private static HashMap itemCache = new HashMap<>(); - - private static int getStackIndex(ItemStack stackA) { - - if (!itemCache.containsKey(stackA)) { - - for (ItemStack item : itemCache.keySet()) { - if (StackInfo.equalItemAndNBT(stackA, item, true)) { - itemCache.put(stackA, itemCache.get(item)); - return itemCache.get(stackA); - } - } - - itemCache.put(stackA, itemCache.size() + fluidCache.size()); - } - - return itemCache.get(stackA); - } - - private static HashMap fluidCache = new HashMap<>(); - - private static int getStackIndex(FluidStack fluidA) { - - if (!fluidCache.containsKey(fluidA)) { - for (FluidStack item : fluidCache.keySet()) { - if (fluidA.isFluidEqual(item)) { - fluidCache.put(fluidA, fluidCache.get(item)); - return fluidCache.get(fluidA); - } - } - - fluidCache.put(fluidA, itemCache.size() + fluidCache.size()); - } - - return fluidCache.get(fluidA); - } - - private static HashMap recipeCache = new HashMap<>(); - - private static int getRecipeIndex(BookmarkRecipeId recipe) { - - if (recipe == null) { - return -1; - } - - if (!recipeCache.containsKey(recipe)) { - for (BookmarkRecipeId item : recipeCache.keySet()) { - if (item.equals(recipe)) { - recipeCache.put(recipe, recipeCache.get(item)); - return recipeCache.get(recipe); - } - } - - recipeCache.put(recipe, recipeCache.size()); - } - - return recipeCache.get(recipe); - } - - } - - protected static class CraftingChainRequest { - - public List items = new ArrayList<>(); - - public HashMap multiplier = new HashMap<>(); - public HashMap counts = new HashMap<>(); - - public HashMap> inputs = new HashMap<>(); - public HashMap> outputs = new HashMap<>(); - - public HashSet initialItems = new HashSet<>(); - public HashSet startedIngrs = new HashSet<>(); - - public CraftingChainRequest(List items, List metadata) { - HashSet inputs = new HashSet<>(); - HashSet outputs = new HashSet<>(); - - for (int index = 0; index < items.size(); index++) { - CraftingChainItem item = new CraftingChainItem(items.get(index), metadata.get(index)); - item.guid = item.ingredient ? "i" + item.guid : "r" + item.guid; - this.items.add(item); - - if (this.multiplier.get(item.recipeIndex) == null) { - this.multiplier.put(item.recipeIndex, 0); - } - - this.counts.put(item.guid, this.counts.getOrDefault(item.guid, 0L) + item.count); - - if (item.ingredient) { - inputs.add(item.recipeIndex); - - if (this.inputs.get(item.stackIndex) == null) { - this.inputs.put(item.stackIndex, new HashSet<>()); - } - - this.inputs.get(item.stackIndex).add(item); - } else { - outputs.add(item.recipeIndex); - - if (this.outputs.get(item.stackIndex) == null) { - this.outputs.put(item.stackIndex, new HashSet<>()); - } - - this.outputs.get(item.stackIndex).add(item); - } - - } - - for (int recipeIndex : this.multiplier.keySet()) { - if (recipeIndex == -1 || !inputs.contains(recipeIndex) || !outputs.contains(recipeIndex)) { - this.initialItems.add(recipeIndex); - } - } - - for (CraftingChainItem item : this.items) { - if (item.ingredient == false && !this.initialItems.contains(item.recipeIndex)) { - this.startedIngrs.add(item.stackIndex); - } - } - - CraftingChainItem.clearStatic(); - } - - } - - public void refresh(List items, List metadata) { - CraftingChainRequest request = new CraftingChainRequest(items, metadata); - HashMap iShift = new HashMap<>(); - HashMap oShift = new HashMap<>(); - boolean change = true; - int iteration = 100; - - while (--iteration > 0 && change) { - change = false; - for (int stackIndex : request.startedIngrs) { - change = calculateShift(request, stackIndex) || change; - } - } - - this.inputs.clear(); - this.outputs.clear(); - this.remainder.clear(); - this.intermediate.clear(); - this.calculatedItems.clear(); - this.multiplier.clear(); - - for (CraftingChainItem item : request.items) { - if (request.initialItems.contains(item.recipeIndex)) { - long iCount = calculateCount(request, request.inputs.get(item.stackIndex)) - - iShift.getOrDefault(item.stackIndex, 0L); - this.calculatedItems.put(item.stack, item.stack); - - if (iCount == 0) { - this.inputs.put(item.stack, item.getItemStack(0)); - } else { - long count = Math.min(iCount, item.count); - this.inputs.put(item.stack, item.getItemStack(count)); - iShift.put(item.stackIndex, iShift.getOrDefault(item.stackIndex, 0L) + count); - } - - } - } - - for (CraftingChainItem item : request.items) { - if (!request.initialItems.contains(item.recipeIndex)) { - long count = item.count + item.factor * request.multiplier.get(item.recipeIndex); - this.calculatedItems.put(item.stack, item.getItemStack(count)); - - if (item.ingredient) { // input - long oCount = calculateCount(request, request.outputs.get(item.stackIndex)) - - oShift.getOrDefault(item.stackIndex, 0L); - - if (oCount < count) { - this.inputs.put(item.stack, item.getItemStack(count - oCount)); - oShift.put(item.stackIndex, oShift.getOrDefault(item.stackIndex, 0L) + oCount); - } else if (oCount >= count) { - this.intermediate.put(item.stack, item.getItemStack(0)); - oShift.put(item.stackIndex, oShift.getOrDefault(item.stackIndex, 0L) + count); - } - - } else { // output - long iCalcCount = calculateCount(request, request.inputs.get(item.stackIndex)); - long iCount = iCalcCount - iShift.getOrDefault(item.stackIndex, 0L); - this.multiplier.put(item.stack, item.factor > 0 ? (int) (count / item.factor) : 0); - - if (iCount >= count) { - this.intermediate.put(item.stack, item.getItemStack(0)); - iShift.put(item.stackIndex, iShift.getOrDefault(item.stackIndex, 0L) + count); - } else if (iCalcCount > 0) { - this.remainder.put(item.stack, item.getItemStack(count - iCount)); - iShift.put(item.stackIndex, iShift.getOrDefault(item.stackIndex, 0L) + iCount); - } else { - this.outputs.put(item.stack, item.getItemStack(count - iCount)); - iShift.put(item.stackIndex, iShift.getOrDefault(item.stackIndex, 0L) + iCount); - } - } - - } - } - - } - - private boolean calculateShift(CraftingChainRequest request, int stackIndex) { - - if (!request.outputs.containsKey(stackIndex)) { - return false; - } - - int minRecipeIndex = 0; - int minShift = Integer.MAX_VALUE; - - for (CraftingChainItem item : request.outputs.get(stackIndex)) { - if (!request.initialItems.contains(item.recipeIndex) && item.factor > 0) { - long ingrCount = calculateCount(request, request.inputs.get(item.stackIndex), item.recipeIndex); - long outputCount = calculateCount(request, request.outputs.get(item.stackIndex)); - long shift = (long) Math.ceil((ingrCount - outputCount) / (double) item.factor); - - if (shift > 0 && shift < minShift) { - minShift = (int) shift; - minRecipeIndex = item.recipeIndex; - } - } - } - - if (minShift < Integer.MAX_VALUE) { - request.multiplier.put(minRecipeIndex, request.multiplier.get(minRecipeIndex) + minShift); - return true; - } - - return false; - } - - private long calculateCount(CraftingChainRequest request, HashSet items) { - return calculateCount(request, items, -1); - } - - private long calculateCount(CraftingChainRequest request, HashSet items, int recipeIndex) { - long count = 0L; - - if (items != null) { - for (CraftingChainItem item : items) { - if (item.recipeIndex != recipeIndex || recipeIndex == -1) { - count += request.counts.get(item.guid) + item.factor * request.multiplier.get(item.recipeIndex); - } - } - } - - return count; - } - -} diff --git a/src/main/java/codechicken/nei/BookmarkPanel.java b/src/main/java/codechicken/nei/BookmarkPanel.java index 1b7874d5..0ad45ef5 100644 --- a/src/main/java/codechicken/nei/BookmarkPanel.java +++ b/src/main/java/codechicken/nei/BookmarkPanel.java @@ -1,12 +1,5 @@ package codechicken.nei; -import static codechicken.lib.gui.GuiDraw.drawRect; -import static codechicken.lib.gui.GuiDraw.fontRenderer; -import static codechicken.lib.gui.GuiDraw.getMousePosition; -import static codechicken.nei.NEIClientUtils.getGuiContainer; -import static codechicken.nei.NEIClientUtils.translate; - -import java.awt.Dimension; import java.awt.Point; import java.io.File; import java.io.FileInputStream; @@ -14,231 +7,45 @@ import java.io.FileOutputStream; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; -import java.nio.charset.StandardCharsets; import java.util.ArrayList; import java.util.Arrays; -import java.util.HashMap; import java.util.HashSet; -import java.util.LinkedHashMap; -import java.util.LinkedList; import java.util.List; import java.util.Map; -import java.util.Optional; -import java.util.WeakHashMap; -import java.util.stream.Collectors; +import java.util.Set; -import javax.annotation.Nullable; - -import net.minecraft.client.gui.FontRenderer; import net.minecraft.client.gui.inventory.GuiContainer; -import net.minecraft.client.renderer.RenderHelper; -import net.minecraft.inventory.Slot; +import net.minecraft.entity.player.InventoryPlayer; import net.minecraft.item.ItemStack; import net.minecraft.nbt.NBTTagCompound; -import net.minecraft.util.EnumChatFormatting; import org.apache.commons.io.IOUtils; -import org.lwjgl.opengl.GL11; -import org.lwjgl.opengl.GL12; - -import com.google.gson.JsonElement; -import com.google.gson.JsonObject; -import com.google.gson.JsonParser; -import com.google.gson.JsonPrimitive; -import com.google.gson.JsonSyntaxException; import codechicken.core.CommonUtils; import codechicken.lib.gui.GuiDraw; -import codechicken.lib.gui.GuiDraw.ITooltipLineHandler; import codechicken.lib.vec.Rectangle4i; -import codechicken.nei.ItemPanel.ItemPanelSlot; import codechicken.nei.api.IBookmarkContainerHandler; -import codechicken.nei.guihook.GuiContainerManager; +import codechicken.nei.bookmark.BookmarkGrid; +import codechicken.nei.bookmark.BookmarkGroup; +import codechicken.nei.bookmark.BookmarkItem; +import codechicken.nei.bookmark.BookmarkStorage; +import codechicken.nei.bookmark.BookmarksGridSlot; +import codechicken.nei.bookmark.GroupingItem; +import codechicken.nei.bookmark.SortableGroup; +import codechicken.nei.bookmark.SortableItem; +import codechicken.nei.recipe.AcceptsFollowingTooltipLineHandler; import codechicken.nei.recipe.BookmarkRecipeId; -import codechicken.nei.recipe.GuiCraftingRecipe; -import codechicken.nei.recipe.GuiRecipe; +import codechicken.nei.recipe.Recipe; +import codechicken.nei.recipe.Recipe.RecipeId; +import codechicken.nei.recipe.Recipe.RecipeIngredient; import codechicken.nei.recipe.StackInfo; -import codechicken.nei.util.NBTJson; -import codechicken.nei.util.ReadableNumberConverter; +import codechicken.nei.recipe.chain.RecipeChainMath; +import codechicken.nei.recipe.chain.RecipeChainTooltipLineHandler; +import codechicken.nei.util.NEIMouseUtils; -public class BookmarkPanel extends PanelWidget { - - protected File bookmarkFile; - protected BookmarkLoadingState bookmarksState; - protected SortableItem sortableItem; - protected SortableGroup sortableGroup; - protected GroupingItem groupingItem; - protected RecipeTooltipLineHandler recipeTooltipLineHandler; - protected CraftingChainTooltipLineHandler craftingChainTooltipLineHandler; - - public Button namespacePrev; - public Button namespaceNext; - public Button pullBookmarkedItems; - public Label namespaceLabel; - - protected List namespaces = new ArrayList<>(); - protected int activeNamespaceIndex = 0; - - protected static class SortableItem { - - public List items = new ArrayList<>(); - public List metadata = new ArrayList<>(); - - public int shiftX = 0; - public int shiftY = 0; - - public SortableItem(List items, List metadata) { - this.items = items; - this.metadata = metadata; - } - - } - - protected static class SortableGroup { - - public int groupId; - - public int shiftX = 0; - public int shiftY = 0; - - public SortableGroup(int groupId) { - this.groupId = groupId; - } - } - - protected static class GroupingItem { - - public boolean ungroup; - - protected int startRowIndex; - protected int startItemIndexTop; - protected int startItemIndexBottom; - - protected int endRowIndex = Integer.MIN_VALUE; - protected int endItemIndexTop; - protected int endItemIndexBottom; - - public GroupingItem(BookmarkGrid BGrid, boolean ungroup, int rowIndex) { - this.ungroup = ungroup; - setStartRowIndex(BGrid, rowIndex); - } - - public void setStartRowIndex(BookmarkGrid BGrid, int rowIndex) { - this.startRowIndex = rowIndex; - this.startItemIndexTop = getTopItemIndex(BGrid, rowIndex); - this.startItemIndexBottom = getBottomItemIndex(BGrid, rowIndex); - } - - public void setEndRowIndex(BookmarkGrid BGrid, int rowIndex) { - - if (BGrid.getRowItemIndex(rowIndex, true) >= 0) { - this.endRowIndex = rowIndex; - this.endItemIndexTop = getTopItemIndex(BGrid, rowIndex); - this.endItemIndexBottom = getBottomItemIndex(BGrid, rowIndex); - } - } - - public boolean hasEndRow() { - return this.endRowIndex != Integer.MIN_VALUE; - } - - protected int getTopItemIndex(BookmarkGrid BGrid, int topRowIndex) { - int topItemIndex = BGrid.getRowItemIndex(topRowIndex, true); - ItemStackMetadata meta = topItemIndex >= 0 ? BGrid.metadata.get(topItemIndex) : null; - - if (meta != null && meta.recipeId != null) { - while (topItemIndex > 0 && meta.equalsRecipe(BGrid.metadata.get(topItemIndex - 1))) { - topItemIndex--; - } - } - - return topItemIndex; - } - - protected int getBottomItemIndex(BookmarkGrid BGrid, int bottomRowIndex) { - int bottomItemIndex = BGrid.getRowItemIndex(bottomRowIndex, false); - ItemStackMetadata meta = bottomItemIndex >= 0 ? BGrid.metadata.get(bottomItemIndex) : null; - - if (meta != null && meta.recipeId != null) { - int size = BGrid.size(); - - while (bottomItemIndex < size - 1 && meta.equalsRecipe(BGrid.metadata.get(bottomItemIndex + 1))) { - bottomItemIndex++; - } - } - - return bottomItemIndex; - } - - public int getTopRowIndex(BookmarkGrid BGrid) { - final List mask = BGrid.getMask(); - final int topItemIndex = Math.min(this.startItemIndexTop, this.endItemIndexTop); - int index = mask.indexOf(topItemIndex); - - if (index != -1) { - return index == -1 ? 0 : index / BGrid.columns; - } - - return 0; - } - - public int getBottomRowIndex(BookmarkGrid BGrid) { - final List mask = BGrid.getMask(); - final int bottomItemIndex = Math.max(this.startItemIndexBottom, this.endItemIndexBottom); - int index = mask.indexOf(bottomItemIndex); - - if (index != -1) { - return index == -1 ? BGrid.getLastRowIndex() : index / BGrid.columns; - } - - return BGrid.getLastRowIndex(); - } - } - - protected static class ItemStackMetadata { - - public int factor; - public int groupId; - public BookmarkRecipeId recipeId; - public boolean ingredient = false; - public boolean fluidDisplay = false; - - public ItemStackMetadata(BookmarkRecipeId recipeId, int factor, boolean ingredient, int groupId, - boolean fluidDisplay) { - this.recipeId = recipeId; - this.factor = factor; - this.ingredient = ingredient; - this.groupId = groupId; - this.fluidDisplay = fluidDisplay; - } - - public ItemStackMetadata(BookmarkRecipeId recipeId, NBTTagCompound nbTag, boolean ingredient, int groupId) { - this(recipeId, nbTag.getInteger("Count"), ingredient, groupId, nbTag.hasKey("gtFluidName")); - } - - public ItemStackMetadata copy() { - return new ItemStackMetadata(this.recipeId, this.factor, this.ingredient, this.groupId, this.fluidDisplay); - } - - public boolean equalsRecipe(ItemStackMetadata meta) { - return equalsRecipe(meta.recipeId, meta.groupId); - } - - public boolean equalsRecipe(BookmarkRecipeId recipeId, int groupId) { - return groupId == this.groupId && recipeId != null && recipeId.equals(this.recipeId); - } - } - - public enum BookmarkViewMode { - DEFAULT, - TODO_LIST - } - - public enum BookmarkLoadingState { - LOADING, - LOADED - } +public class BookmarkPanel extends PanelWidget { + @Deprecated public static class BookmarkRecipe { public String handlerName = ""; @@ -258,1161 +65,44 @@ public class BookmarkPanel extends PanelWidget { return recipeId; } - } - protected static class BookmarkGroup { + public Recipe getRecipe() { + String handlerName = this.handlerName; - public BookmarkCraftingChain crafting = null; - public BookmarkViewMode viewMode; - - public BookmarkGroup(BookmarkViewMode viewMode) { - this.viewMode = viewMode; - } - - public BookmarkGroup(BookmarkViewMode viewMode, boolean crafting) { - this.viewMode = viewMode; - this.crafting = crafting ? new BookmarkCraftingChain() : null; - } - - public void toggleViewMode() { - if (this.viewMode == BookmarkViewMode.DEFAULT) { - this.viewMode = BookmarkViewMode.TODO_LIST; - } else { - this.viewMode = BookmarkViewMode.DEFAULT; + if (handlerName.isEmpty() && recipeId != null) { + handlerName = recipeId.handlerName; } - } - public void toggleCraftingMode() { - if (this.crafting == null) { - this.crafting = new BookmarkCraftingChain(); - } else { - this.crafting = null; - } - } - - public BookmarkGroup copy() { - return new BookmarkGroup(this.viewMode); + return Recipe.of(this.result, handlerName, this.ingredients); } } - public static class BookmarkGrid extends ItemsGrid { - - protected static final int GROUP_PANEL_WIDTH = 7; - protected static final int DEFAULT_GROUP_ID = 0; - protected static final float SCALE_SPEED = 0.1f; - - protected List metadata = new ArrayList<>(); - protected WeakHashMap animation = new WeakHashMap<>(); - - protected Map groups = new HashMap<>(); - protected List gridGroupMask = new ArrayList<>(); - protected int previousPageGroupId = DEFAULT_GROUP_ID; - protected int nextPageGroupId = DEFAULT_GROUP_ID; - protected int focusedGroupId = -1; - protected int pageCount = 0; - - public BookmarkGrid() { - this.groups.put(DEFAULT_GROUP_ID, new BookmarkGroup(BookmarkViewMode.DEFAULT)); - } - - public BookmarkViewMode getViewMode(int groupId) { - return this.groups.get(groupId).viewMode; - } - - public void setViewMode(int groupId, BookmarkViewMode mode) { - if (this.groups.get(groupId).viewMode != mode) { - this.groups.get(groupId).viewMode = mode; - if (mode == BookmarkViewMode.DEFAULT) { - sortGroup(groupId); - } - - onItemsChanged(); - } - } - - public void toggleViewMode(int groupId) { - this.groups.get(groupId).toggleViewMode(); - if (this.groups.get(groupId).viewMode == BookmarkViewMode.DEFAULT) { - sortGroup(groupId); - } - onItemsChanged(); - } - - public boolean getCraftingMode(int groupId) { - return this.groups.get(groupId).crafting != null; - } - - public void setCraftingMode(int groupId, boolean on) { - if ((this.groups.get(groupId).crafting != null) != on) { - this.groups.get(groupId).crafting = on ? new BookmarkCraftingChain() : null; - onItemsChanged(); - } - } - - public void toggleCraftingMode(int groupId) { - this.groups.get(groupId).toggleCraftingMode(); - onItemsChanged(); - } - - @Override - public int getNumPages() { - - if (gridMask == null) { - getMask(); - } - - return this.pageCount; - } - - @Override - protected List getMask() { - - if (this.gridMask != null) { - return this.gridMask; - } - - if (this.perPage == 0 || size() == 0) { - this.gridGroupMask = new ArrayList<>(); - this.gridMask = new ArrayList<>(); - return this.gridMask; - } - - ItemStackMetadata previousMeta = new ItemStackMetadata(null, 1, false, DEFAULT_GROUP_ID, false); - List itemsMask = new ArrayList<>(); - List groupsMask = new ArrayList<>(); - ItemStackMetadata meta; - int size = size(); - int lastIdx = -1; - int index = 0; - int idx = 0; - - while (idx < size && lastIdx != idx) { - lastIdx = idx; - - for (int r = 0; r < rows && idx < size; r++) { - for (int c = 0; c < columns && idx < size; c++) { - index = r * columns + c; - - if (isInvalidSlot(index)) { - itemsMask.add(null); - } else { - meta = this.metadata.get(idx); - - if (c > 0 && previousMeta.groupId != meta.groupId) { - // new group must start on a new line - itemsMask.add(null); - } else if (getViewMode(meta.groupId) == BookmarkViewMode.DEFAULT) { - previousMeta = meta; - itemsMask.add(idx++); - } else { - - if (c == 0 && (meta.recipeId == null || meta.ingredient == false - || index + 1 < rows * columns && isInvalidSlot(index + 1) - || previousMeta.groupId != meta.groupId - || meta.ingredient && !meta.recipeId.equals(previousMeta.recipeId))) { - // In first column must be an item without recipe, a recipe result, or an ingredient - // if the second column is occupied - previousMeta = meta; - itemsMask.add(idx++); - } else - if (c > 0 && meta.recipeId != null && meta.recipeId.equals(previousMeta.recipeId)) { - previousMeta = meta; - itemsMask.add(idx++); - } else { - itemsMask.add(null); - } - - } - - } - - } - groupsMask.add(previousMeta.groupId); - } - } - - this.pageCount = (int) Math.ceil(itemsMask.size() / (float) (this.rows * this.columns)); - this.page = Math.max(0, Math.min(this.page, this.pageCount - 1)); - this.gridMask = itemsMask - .subList(page * rows * columns, Math.min(itemsMask.size(), (page + 1) * rows * columns)); - this.previousPageGroupId = DEFAULT_GROUP_ID; - this.nextPageGroupId = DEFAULT_GROUP_ID; - - if (page > 0 && (this.page * this.rows) < groupsMask.size() - && groupsMask.get(this.page * this.rows) == groupsMask.get(this.page * this.rows - 1)) { - this.previousPageGroupId = groupsMask.get(this.page * this.rows); - } - - if ((this.page + 1) * this.rows < groupsMask.size() - && groupsMask.get((this.page + 1) * this.rows) == groupsMask.get((this.page + 1) * this.rows - 1)) { - this.nextPageGroupId = groupsMask.get((this.page + 1) * this.rows); - } - - this.gridGroupMask = groupsMask.subList(page * rows, Math.min(groupsMask.size(), (page + 1) * rows)); - - return this.gridMask; - } - - protected int getRowGroupId(int rowIndex) { - if (gridMask == null) { - getMask(); - } - - return rowIndex != -1 && rowIndex < this.gridGroupMask.size() ? this.gridGroupMask.get(rowIndex) - : DEFAULT_GROUP_ID; - } - - protected int getRowItemIndex(int rowIndex, boolean dir) { - final List mask = getMask(); - final int size = mask.size(); - int i = dir ? 0 : columns - 1; - - while (i >= 0 && i < columns) { - - if ((rowIndex * columns + i) < size && mask.get(rowIndex * columns + i) != null) { - return mask.get(rowIndex * columns + i); - } - - i += dir ? 1 : -1; - } - - return -1; - } - - protected int getHoveredRowIndex(boolean groupPanel) { - final Point mouse = getMousePosition(); - final int leftBorder = marginLeft + paddingLeft; - final int r = (int) ((mouse.y - marginTop) / SLOT_SIZE); - - if (!(new Rectangle4i( - leftBorder - (groupPanel ? BookmarkGrid.GROUP_PANEL_WIDTH : 0), - marginTop, - columns * SLOT_SIZE, - (getLastRowIndex() + 1) * SLOT_SIZE)).contains(mouse.x, mouse.y)) { - return -1; - } - - if (groupPanel && mouse.x >= leftBorder - GROUP_PANEL_WIDTH && mouse.x < leftBorder) { - return r; - } - - if (!groupPanel && !isInvalidSlot(columns * r + ((mouse.x - leftBorder) / SLOT_SIZE))) { - return r; - } - - return -1; - } - - @Override - public void setGridSize(int mleft, int mtop, int w, int h) { - super.setGridSize(mleft + GROUP_PANEL_WIDTH, mtop, w, h); - } - - @Override - public void draw(int mousex, int mousey) { - if (getPerPage() == 0) { - return; - } - - List groupMask = this.gridGroupMask; - final GroupingItem groupingItem = LayoutManager.bookmarkPanel.groupingItem; - final int sortableGroupId = LayoutManager.bookmarkPanel.sortableGroup != null - ? LayoutManager.bookmarkPanel.sortableGroup.groupId - : -3; - int previousPageGroupId = this.previousPageGroupId; - int nextPageGroupId = this.nextPageGroupId; - int previoudGroupId = DEFAULT_GROUP_ID; - int groupStartIndex = -2; - - if (sortableGroupId != -3) { - for (int rowIndex = 0; rowIndex < groupMask.size(); rowIndex++) { - if (groupMask.get(rowIndex) == sortableGroupId) { - drawRect( - marginLeft + paddingLeft - GROUP_PANEL_WIDTH, - marginTop + rowIndex * SLOT_SIZE, - GROUP_PANEL_WIDTH, - SLOT_SIZE, - 0x66555555); - } - } - } else { - final int focusedRowIndex = getHoveredRowIndex(true); - - if (focusedRowIndex != -1) { - drawRect( - marginLeft + paddingLeft - GROUP_PANEL_WIDTH, - marginTop + focusedRowIndex * SLOT_SIZE, - GROUP_PANEL_WIDTH, - SLOT_SIZE, - 0xee555555); - } - } - - if (groupingItem != null && groupingItem.hasEndRow()) { - final List mask = this.getMask(); - int topRowIndex = groupingItem.getTopRowIndex(this); - int bottomRowIndex = groupingItem.getBottomRowIndex(this); - int groupIdA = this.metadata.get(groupingItem.startItemIndexTop).groupId; - int groupId = groupingItem.ungroup ? DEFAULT_GROUP_ID : (groupIdA == DEFAULT_GROUP_ID ? -1 : groupIdA); - groupMask = new ArrayList<>(groupMask); - - if (mask.indexOf(Math.min(groupingItem.startItemIndexTop, groupingItem.endItemIndexTop)) == -1) { - previousPageGroupId = groupId; - } - - if (mask.indexOf(Math.max(groupingItem.startItemIndexBottom, groupingItem.endItemIndexBottom)) == -1) { - nextPageGroupId = groupId; - } - - for (int rowIndex = topRowIndex; rowIndex <= bottomRowIndex; rowIndex++) { - groupMask.set(rowIndex, groupId); - } - - if (groupIdA != DEFAULT_GROUP_ID && !groupingItem.ungroup - && groupingItem.startItemIndexTop != groupingItem.endItemIndexTop) { - if (groupingItem.endItemIndexTop < groupingItem.startItemIndexTop) { - for (int rowIndex = topRowIndex - 1; rowIndex >= 0 - && groupMask.get(rowIndex) == groupIdA; rowIndex--) { - groupMask.set(rowIndex, DEFAULT_GROUP_ID); - } - } else if (groupingItem.endItemIndexTop > groupingItem.startItemIndexTop) { - for (int rowIndex = bottomRowIndex + 1; rowIndex < groupMask.size() - && groupMask.get(rowIndex) == groupIdA; rowIndex++) { - groupMask.set(rowIndex, DEFAULT_GROUP_ID); - } - } - } - - } - - if (previousPageGroupId != DEFAULT_GROUP_ID && previousPageGroupId == groupMask.get(0)) { - previoudGroupId = previousPageGroupId; - groupStartIndex = -1; - } - - for (int rowIndex = 0; rowIndex < groupMask.size(); rowIndex++) { - int groupId = groupMask.get(rowIndex); - - if (groupStartIndex != -2 && previoudGroupId != DEFAULT_GROUP_ID && previoudGroupId != groupId) { - if (previoudGroupId != sortableGroupId) { - drawGroup(Math.max(DEFAULT_GROUP_ID, previoudGroupId), groupStartIndex, rowIndex - 1); - } - groupStartIndex = -2; - } - - if (groupStartIndex == -2 && groupId != DEFAULT_GROUP_ID) { - groupStartIndex = rowIndex; - } - - previoudGroupId = groupId; - } - - if (groupStartIndex != -2) { - final int rowIndex = nextPageGroupId != DEFAULT_GROUP_ID - && nextPageGroupId == groupMask.get(groupMask.size() - 1) ? this.rows : getLastRowIndex(); - if (previoudGroupId != sortableGroupId) { - drawGroup(Math.max(DEFAULT_GROUP_ID, previoudGroupId), groupStartIndex, rowIndex); - } - } - - if (NEIClientUtils.shiftKey() && !LayoutManager.bookmarkPanel.inEditingState()) { - final int rowId = getHoveredRowIndex(true); - - if (rowId != -1) { - this.focusedGroupId = getRowGroupId(rowId); - } else { - final ItemPanelSlot focused = getSlotMouseOver(mousex, mousey); - this.focusedGroupId = focused != null ? this.metadata.get(focused.slotIndex).groupId : -1; - } - - } else { - this.focusedGroupId = -1; - } - - super.draw(mousex, mousey); - } - - private void drawGroup(int groupId, int rowIndexStart, int rowIndexEnd) { - final int halfWidth = GROUP_PANEL_WIDTH / 2; - final int heightPadding = SLOT_SIZE / 4; - final int leftPosition = marginLeft + paddingLeft - halfWidth - 1; - final int color = this.groups.get(groupId).crafting != null ? 0x6645DA75 : 0xff666666; - int width = (Math.min(rowIndexEnd, this.rows - 1) - Math.max(0, rowIndexStart) + 1) * SLOT_SIZE; - int top = marginTop + Math.max(0, rowIndexStart) * SLOT_SIZE; - - if (rowIndexStart >= 0) { - drawRect(leftPosition, marginTop + rowIndexStart * SLOT_SIZE + heightPadding, halfWidth, 1, color); - top += heightPadding + 1; - width -= heightPadding + 1; - } - - if (rowIndexEnd < this.rows) { - drawRect(leftPosition, marginTop + (rowIndexEnd + 1) * SLOT_SIZE - heightPadding, halfWidth, 1, color); - width -= heightPadding; - } - - drawRect(leftPosition, top, 1, width, color); - } - - private void removeDuplicateItems() { - final HashMap recipeCache = new HashMap<>(); - final HashMap itemsCache = new HashMap<>(); - final HashSet unique = new HashSet<>(); - ItemStackMetadata meta; - ItemStack stack; - int index = 0; - String key; - - while (index < this.metadata.size()) { - stack = this.realItems.get(index); - meta = this.metadata.get(index); - key = String.valueOf(meta.groupId); - - if (!itemsCache.containsKey(stack)) { - for (ItemStack item : itemsCache.keySet()) { - if (StackInfo.equalItemAndNBT(stack, item, true)) { - itemsCache.put(stack, itemsCache.get(item)); - break; - } - } - } - - if (!itemsCache.containsKey(stack)) { - itemsCache.put(stack, itemsCache.size()); - } - - key = key + ":" + itemsCache.get(stack); - - if (meta.recipeId != null) { - if (!recipeCache.containsKey(meta.recipeId)) { - for (BookmarkRecipeId item : recipeCache.keySet()) { - if (item.equals(meta.recipeId)) { - recipeCache.put(meta.recipeId, recipeCache.get(item)); - break; - } - } - } - - if (!recipeCache.containsKey(meta.recipeId)) { - recipeCache.put(meta.recipeId, recipeCache.size()); - } - - key = key + ":" + recipeCache.get(meta.recipeId); - } - - if (unique.contains(key)) { - this.realItems.remove(index); - this.metadata.remove(index); - } else { - unique.add(key); - index++; - } - } - - } - - private void sortGroup(int groupId) { - final int dir = getViewMode(groupId) == BookmarkViewMode.TODO_LIST ? 1 : -1; - final int size = this.metadata.size(); - ItemStackMetadata meta; - int idx = 0; - - while (idx < size) { - meta = this.metadata.get(idx); - if (meta.groupId == groupId && meta.recipeId != null) { - final HashMap sortingRank = new HashMap<>(); - final ArrayList sortedMetadata = new ArrayList<>(); - final ArrayList sortedItems = new ArrayList<>(); - final ArrayList items = new ArrayList<>(); - - for (int index = idx; index < size; index++) { - if (meta.equalsRecipe(this.metadata.get(index))) { - sortingRank.put(index, (this.metadata.get(index).ingredient ? 1 : -1) * dir); - items.add(index); - } - } - - items.sort((a, b) -> sortingRank.get(a) - sortingRank.get(b)); - - for (int index : items) { - sortedItems.add(this.realItems.get(index)); - sortedMetadata.add(this.metadata.get(index)); - } - - this.realItems.removeAll(sortedItems); - this.metadata.removeAll(sortedMetadata); - - this.realItems.addAll(idx, sortedItems); - this.metadata.addAll(idx, sortedMetadata); - - idx += sortedItems.size(); - } else { - idx++; - } - } - - } - - private void sortIngredients() { - if (this.size() == 0) { - return; - } - - removeDuplicateItems(); - boolean inEditingState = LayoutManager.bookmarkPanel.inEditingState(); - - for (int groupId : this.groups.keySet()) { - BookmarkGroup group = this.groups.get(groupId); - - if (group.viewMode == BookmarkViewMode.TODO_LIST) { - sortGroup(groupId); - } - - if (group.crafting != null && !inEditingState) { - ArrayList groupMetadata = new ArrayList<>(); - ArrayList groupItems = new ArrayList<>(); - - for (int idx = 0; idx < this.metadata.size(); idx++) { - if (this.metadata.get(idx).groupId == groupId) { - groupItems.add(this.realItems.get(idx)); - groupMetadata.add(this.metadata.get(idx)); - } - } - - group.crafting.refresh(groupItems, groupMetadata); - } - - } - } - - protected void onItemsChanged() { - sortIngredients(); - onGridChanged(); - } - - protected void createGroup(GroupingItem groupingItem) { - final int topItemIndex = Math.min(groupingItem.startItemIndexTop, groupingItem.endItemIndexTop); - final int bottomItemIndex = Math.max(groupingItem.startItemIndexBottom, groupingItem.endItemIndexBottom); - final int groupIdA = this.metadata.get(groupingItem.startItemIndexTop).groupId; - int groupId = DEFAULT_GROUP_ID; - - if (!groupingItem.ungroup) { - if (groupIdA == DEFAULT_GROUP_ID) { - for (ItemStackMetadata meta : this.metadata) { - if (meta.groupId > groupId) { - groupId = meta.groupId; - } - } - this.groups.put(++groupId, new BookmarkGroup(getViewMode(DEFAULT_GROUP_ID))); - } else { - groupId = groupIdA; - } - } - - for (int idx = topItemIndex; idx <= bottomItemIndex; idx++) { - this.metadata.get(idx).groupId = groupId; - } - - if (groupIdA != DEFAULT_GROUP_ID) { - - if (!groupingItem.ungroup) { - if (groupingItem.endItemIndexTop < groupingItem.startItemIndexTop) { - for (int idx = topItemIndex - 1; idx >= 0 - && this.metadata.get(idx).groupId == groupIdA; idx--) { - this.metadata.get(idx).groupId = DEFAULT_GROUP_ID; - } - } else { - for (int idx = bottomItemIndex + 1; idx < this.metadata.size() - && this.metadata.get(idx).groupId == groupIdA; idx++) { - this.metadata.get(idx).groupId = DEFAULT_GROUP_ID; - } - } - } else if (bottomItemIndex + 1 < this.metadata.size() - && this.metadata.get(bottomItemIndex + 1).groupId == groupIdA) { - this.groups.put(++groupId, new BookmarkGroup(getViewMode(groupIdA))); - - for (int idx = bottomItemIndex + 1; idx < this.metadata.size() - && this.metadata.get(idx).groupId == groupIdA; idx++) { - this.metadata.get(idx).groupId = groupId; - } - } - } - - final HashSet usedSetIds = new HashSet<>(); - for (ItemStackMetadata meta : this.metadata) { - usedSetIds.add(meta.groupId); - } - - this.groups.keySet().removeIf((Integer k) -> k != DEFAULT_GROUP_ID && !usedSetIds.contains(k)); - - onItemsChanged(); - } - - protected void removeGroup(int groupId) { - - for (int i = this.metadata.size() - 1; i >= 0; i--) { - if (this.metadata.get(i).groupId == groupId) { - this.metadata.remove(i); - this.realItems.remove(i); - } - } - - if (groupId != DEFAULT_GROUP_ID) { - this.groups.remove(groupId); - } - - onItemsChanged(); - } - - protected void moveGroup(int groupId, int overRowIndex) { - final int overGroupId = getRowGroupId(overRowIndex); - final int overItemIndex = getRowItemIndex(overRowIndex, false); - final List items = new ArrayList<>(); - final List metadata = new ArrayList<>(); - boolean moveDown = true; - - for (int idx = overItemIndex + 1; idx < this.realItems.size() && moveDown; idx++) { - moveDown = this.metadata.get(idx).groupId != groupId; - } - - for (int idx = 0; idx < this.realItems.size(); idx++) { - if (this.metadata.get(idx).groupId == groupId) { - items.add(this.realItems.get(idx)); - metadata.add(this.metadata.get(idx)); - } - } - - if (moveDown) { - int bottomItemIndex = overItemIndex + 1; - - if (overGroupId == BookmarkGrid.DEFAULT_GROUP_ID || bottomItemIndex == this.realItems.size() - || this.metadata.get(bottomItemIndex).groupId != overGroupId) { - this.realItems.removeAll(items); - this.metadata.removeAll(metadata); - - bottomItemIndex -= items.size(); - - this.realItems.addAll(bottomItemIndex, items); - this.metadata.addAll(bottomItemIndex, metadata); - onItemsChanged(); - } - - } else { - int topItemIndex = getRowItemIndex(overRowIndex, true) - 1; - - if (topItemIndex >= -1 && (overGroupId == BookmarkGrid.DEFAULT_GROUP_ID || topItemIndex == -1 - || this.metadata.get(topItemIndex).groupId != overGroupId)) { - this.realItems.removeAll(items); - this.metadata.removeAll(metadata); - - this.realItems.addAll(topItemIndex + 1, items); - this.metadata.addAll(topItemIndex + 1, metadata); - - onItemsChanged(); - } - } - - } - - public int indexOf(ItemStack stackA, BookmarkRecipeId recipeId) { - return indexOf(stackA, recipeId, DEFAULT_GROUP_ID); - } - - public int indexOf(ItemStack stackA, BookmarkRecipeId recipeId, int groupId) { - - for (int idx = 0; idx < realItems.size(); idx++) { - final ItemStackMetadata meta = getMetadata(idx); - if (meta.groupId == groupId - && (recipeId == null && meta.recipeId == null - || recipeId != null && recipeId.equals(meta.recipeId)) - && StackInfo.equalItemAndNBT(stackA, getItem(idx), true)) { - return idx; - } - } - - return -1; - } - - public ItemStack getItem(int idx) { - final ItemStack stack = super.realItems.get(idx); - final ItemStackMetadata meta = this.getMetadata(idx); - final BookmarkCraftingChain crafting = this.groups.get(meta.groupId).crafting; - - if (crafting != null && crafting.calculatedItems.containsKey(stack)) { - return crafting.calculatedItems.get(stack); - } - - return stack; - } - - public ItemStackMetadata getMetadata(int idx) { - return metadata.get(idx); - } - - public void addItem(ItemStack stackA, ItemStackMetadata meta) { - addItem(stackA, meta, true); - } - - public void addItem(ItemStack stackA, ItemStackMetadata meta, boolean animate) { - this.realItems.add(stackA); - this.metadata.add(meta); - - if (animate && NEIClientConfig.getGridRenderingCacheMode() == 0 && NEIClientConfig.areBookmarksAnimated()) { - this.animation.put(stackA, 0f); - } - - onItemsChanged(); - } - - protected void removeRecipe(int idx, boolean removeFullRecipe) { - final ItemStackMetadata meta = this.metadata.get(idx); - - if (!removeFullRecipe && meta.recipeId != null && !meta.ingredient) { - final Optional result = this.metadata.stream() - .filter(m -> m != meta && !m.ingredient && meta.equalsRecipe(m)).findAny(); - removeFullRecipe = !result.isPresent(); - } - - if (meta.recipeId != null && removeFullRecipe) { - removeRecipe(meta.recipeId, meta.groupId); - } else { - removeItem(idx); - } - } - - protected boolean removeRecipe(BookmarkRecipeId recipeIdA, int groupId) { - boolean removed = false; - - for (int slotIndex = metadata.size() - 1; slotIndex >= 0; slotIndex--) { - if (metadata.get(slotIndex).equalsRecipe(recipeIdA, groupId)) { - removed = removeItem(slotIndex) || removed; - } - } - - return removed; - } - - protected boolean removeItem(int idx) { - realItems.remove(idx); - metadata.remove(idx); - onItemsChanged(); - return true; - } - - public BookmarkRecipeId getRecipeId(int idx) { - return getMetadata(idx).recipeId; - } - - protected void moveItem(SortableItem sortableItem, int slotIndex) { - moveItem( - sortableItem, - slotIndex, - slotIndex < 0 ? DEFAULT_GROUP_ID : this.metadata.get(slotIndex).groupId, - slotIndex < this.realItems.indexOf(sortableItem.items.get(0))); - } - - protected void moveItem(SortableItem sortableItem, int slotIndex, int groupId, boolean moveUp) { - if (slotIndex == -1) return; - - if (sortableItem.items.indexOf(this.realItems.get(slotIndex)) == -1) { - final ItemStack stackA = this.realItems.get(slotIndex); - - this.realItems.removeAll(sortableItem.items); - this.metadata.removeAll(sortableItem.metadata); - - slotIndex = this.realItems.indexOf(stackA) + (moveUp ? 0 : 1); - - for (ItemStackMetadata sm : sortableItem.metadata) { - sm.groupId = groupId; - } - - this.realItems.addAll(slotIndex, sortableItem.items); - this.metadata.addAll(slotIndex, sortableItem.metadata); - - onItemsChanged(); - } else if (sortableItem.metadata.get(0).groupId != groupId) { - - for (ItemStackMetadata meta : sortableItem.metadata) { - meta.groupId = groupId; - } - - onItemsChanged(); - } - } - - @Override - protected int getGridRenderingCacheMode() { - return this.focusedGroupId == -1 ? NEIClientConfig.getGridRenderingCacheMode() : 0; - } - - @Override - protected void beforeDrawSlot(@Nullable ItemPanelSlot focus, int idx, Rectangle4i rect) { - - if (LayoutManager.bookmarkPanel.sortableGroup != null - && this.getMetadata(idx).groupId == LayoutManager.bookmarkPanel.sortableGroup.groupId) { - drawRect(rect.x, rect.y, rect.w, rect.h, 0x66555555); - } else if (LayoutManager.bookmarkPanel.sortableItem != null - && LayoutManager.bookmarkPanel.sortableItem.items.contains(this.realItems.get(idx))) { - drawRect(rect.x, rect.y, rect.w, rect.h, 0x66555555); - } else - if (!LayoutManager.bookmarkPanel.inEditingState()) { - - if (NEIClientUtils.shiftKey()) { - ItemStack stack = this.realItems.get(idx); - ItemStackMetadata meta = this.getMetadata(idx); - BookmarkGroup groupMeta = this.groups.get(meta.groupId); - - if (groupMeta.crafting != null && meta.groupId == this.focusedGroupId) { - - if (groupMeta.crafting.inputs.containsKey(stack)) { - drawRect(rect.x, rect.y, rect.w, rect.h, 0x6645DA75); // inputs - } else if (groupMeta.crafting.outputs.containsKey(stack)) { - drawRect(rect.x, rect.y, rect.w, rect.h, 0x9966CCFF); // exports - } else if (groupMeta.crafting.remainder.containsKey(stack)) { - drawRect(rect.x, rect.y, rect.w, rect.h, 0x9966CCFF); // exports - } - - } else if (focus != null && meta.equalsRecipe(getMetadata(focus.slotIndex))) { - drawRect(rect.x, rect.y, rect.w, rect.h, meta.ingredient ? 0x6645DA75 : 0x9966CCFF); // highlight - // recipe - } else { - super.beforeDrawSlot(focus, idx, rect); - } - - } else { - super.beforeDrawSlot(focus, idx, rect); - } - } - - } - - @Override - protected void afterDrawSlot(@Nullable ItemPanelSlot focus, int idx, Rectangle4i rect) { - final ItemStackMetadata meta = this.getMetadata(idx); - - if (meta.ingredient == true || meta.recipeId == null - || LayoutManager.bookmarkPanel.sortableItem != null - || LayoutManager.bookmarkPanel.sortableGroup != null) { - return; - } - - final ItemStack stack = this.realItems.get(idx); - final BookmarkGroup groupMeta = this.groups.get(meta.groupId); - int multiplier = 0; - - if (NEIClientUtils.shiftKey()) { - final ItemStackMetadata prevMeta = idx > 0 ? this.getMetadata(idx - 1) : null; - if (prevMeta == null || prevMeta.ingredient || !meta.recipeId.equals(prevMeta.recipeId)) { - if (groupMeta.crafting != null && meta.groupId == this.focusedGroupId - && groupMeta.crafting.multiplier.containsKey(stack)) { - multiplier = this.groups.get(meta.groupId).crafting.multiplier.get(stack); - } else if (focus != null && meta.factor > 0 && meta.equalsRecipe(getMetadata(focus.slotIndex))) { - multiplier = StackInfo.itemStackToNBT(stack).getInteger("Count") / meta.factor; - } - } - } - - if (multiplier > 0) { - drawRecipeMarker( - rect.x, - rect.y, - GuiContainerManager.getFontRenderer(stack), - "x" + ReadableNumberConverter.INSTANCE.toWideReadableForm(multiplier), - 16777215); - } else if (meta.recipeId != null && !meta.ingredient && NEIClientConfig.showRecipeMarker()) { - drawRecipeMarker(rect.x, rect.y, GuiContainerManager.getFontRenderer(stack), "R", 0xA0A0A0); - } - - } - - @Override - protected void drawItem(Rectangle4i rect, int idx) { - - if ((LayoutManager.bookmarkPanel.sortableGroup == null - || this.metadata.get(idx).groupId != LayoutManager.bookmarkPanel.sortableGroup.groupId) - && (LayoutManager.bookmarkPanel.sortableItem == null - || !LayoutManager.bookmarkPanel.sortableItem.items.contains(this.realItems.get(idx)))) { - ItemStack realStack = this.realItems.get(idx); - final ItemStackMetadata meta = this.getMetadata(idx); - final BookmarkGroup groupMeta = this.groups.get(meta.groupId); - ItemStack drawStack = realStack; - - if (groupMeta.crafting != null && meta.groupId == this.focusedGroupId) { - - if (groupMeta.crafting.inputs.containsKey(drawStack)) { - drawStack = groupMeta.crafting.inputs.get(drawStack); - } else if (groupMeta.crafting.outputs.containsKey(drawStack)) { - drawStack = groupMeta.crafting.outputs.get(drawStack); - } else if (groupMeta.crafting.remainder.containsKey(drawStack)) { - drawStack = groupMeta.crafting.remainder.get(drawStack); - } else if (groupMeta.crafting.intermediate.containsKey(drawStack)) { - drawStack = groupMeta.crafting.intermediate.get(drawStack); - } - - } else if (groupMeta.crafting != null && groupMeta.crafting.calculatedItems.containsKey(drawStack)) { - drawStack = groupMeta.crafting.calculatedItems.get(drawStack); - } - - String stackSize = meta.fluidDisplay || drawStack.stackSize == 0 ? "" - : ReadableNumberConverter.INSTANCE.toWideReadableForm(drawStack.stackSize); - - if (this.animation.containsKey(realStack) && this.animation.get(realStack) < 1) { - final float currentScale = this.animation.get(realStack) + SCALE_SPEED; - - if (currentScale >= 1) { - this.animation.remove(realStack); - } else { - this.animation.put(realStack, currentScale); - } - - drawPoppingItem(rect, drawStack, stackSize, currentScale); - } else { - GuiContainerManager.drawItem(rect.x + 1, rect.y + 1, drawStack, true, stackSize); - } - - } - - } - - protected void drawPoppingItem(Rectangle4i rect, ItemStack stack, String stackSize, float currentScale) { - GL11.glScalef(currentScale, currentScale, currentScale); // push & pop matrix crashes the game - - GuiContainerManager.drawItem( - Math.round((rect.x + 1 + (SLOT_SIZE / 2 - currentScale * SLOT_SIZE / 2)) / currentScale), - Math.round((rect.y + 1 + (SLOT_SIZE / 2 - currentScale * SLOT_SIZE / 2)) / currentScale), - stack, - true, - stackSize); - - GL11.glScalef(1 / currentScale, 1 / currentScale, 1 / currentScale); - } - - protected void drawRecipeMarker(int offsetX, int offsetY, FontRenderer fontRenderer, String text, int color) { - final float scaleFactor = fontRenderer.getUnicodeFlag() ? 0.85f : 0.5f; - final float inverseScaleFactor = 1.0f / scaleFactor; - - GuiContainerManager.enable2DRender(); - GL11.glScaled(scaleFactor, scaleFactor, scaleFactor); - - final int X = (int) (((float) offsetX + 1.0f) * inverseScaleFactor); - final int Y = (int) (((float) offsetY + 1.0f) * inverseScaleFactor); - fontRenderer.drawStringWithShadow(text, X, Y, color); - - GL11.glScaled(inverseScaleFactor, inverseScaleFactor, inverseScaleFactor); - GuiContainerManager.enable3DRender(); - } - - @Override - protected void afterDrawItems(int mousex, int mousey, @Nullable ItemPanelSlot focused) { - super.afterDrawItems(mousex, mousey, focused); - - if (focused != null && NEIClientConfig.getRecipeTooltipsMode() != 0 && NEIClientConfig.isLoaded()) { - final ItemStackMetadata meta = this.metadata.get(focused.slotIndex); - final int tooltipMode = NEIClientConfig.getRecipeTooltipsMode(); - - if (meta.recipeId == null || meta.ingredient) { - LayoutManager.bookmarkPanel.recipeTooltipLineHandler = null; - } else if (this.groups.get(meta.groupId).viewMode == BookmarkViewMode.DEFAULT && tooltipMode != 1 - && tooltipMode != 3) { - LayoutManager.bookmarkPanel.recipeTooltipLineHandler = null; - } else - if (this.groups.get(meta.groupId).viewMode == BookmarkViewMode.TODO_LIST && tooltipMode != 2 - && tooltipMode != 3) { - LayoutManager.bookmarkPanel.recipeTooltipLineHandler = null; - } else - if (LayoutManager.bookmarkPanel.recipeTooltipLineHandler == null - || LayoutManager.bookmarkPanel.recipeTooltipLineHandler.recipeId != meta.recipeId) { - LayoutManager.bookmarkPanel.recipeTooltipLineHandler = new RecipeTooltipLineHandler( - focused.item, - meta.recipeId); - } - - } else { - LayoutManager.bookmarkPanel.recipeTooltipLineHandler = null; - } - - } - - public void update() { - - if (LayoutManager.bookmarkPanel.recipeTooltipLineHandler != null) { - LayoutManager.bookmarkPanel.recipeTooltipLineHandler.update(); - } - - } - + public enum BookmarkViewMode { + DEFAULT, + TODO_LIST } - protected static class RecipeTooltipLineHandler implements ITooltipLineHandler { + protected BookmarkStorage storage = new BookmarkStorage(); - protected ItemStack stack = null; - protected GuiRecipe gui = null; - protected BookmarkRecipeId recipeId = null; - protected boolean createdGui = false; + public SortableItem sortableItem; + public SortableGroup sortableGroup; + public GroupingItem groupingItem; - public RecipeTooltipLineHandler(ItemStack stack, BookmarkRecipeId recipeId) { - this.stack = stack; - this.recipeId = recipeId; - } + protected AcceptsFollowingTooltipLineHandler acceptsFollowingTooltipLineHandler; + protected RecipeChainTooltipLineHandler recipeChainTooltipLineHandler; - public void update() { - - if (this.gui == null && !this.createdGui) { - this.gui = GuiCraftingRecipe.createRecipeGui("recipeId", false, this.stack, this.recipeId); - this.createdGui = true; - - if (this.gui != null) { - this.gui.initGui(); - this.gui.guiTop = 0; - this.gui.guiLeft = 0; - } - } - } - - @Override - public Dimension getSize() { - - if (this.gui != null) { - return this.gui.getWidgetSize(); - } - - return new Dimension(0, 0); - } - - @Override - public void draw(int x, int y) { - if (this.gui == null) return; - - GL11.glPushMatrix(); - GL11.glScaled(1, 1, 3); - GL11.glTranslatef(x, y, 0); - GL11.glPushAttrib(GL11.GL_ALL_ATTRIB_BITS); - RenderHelper.disableStandardItemLighting(); - - this.gui.drawGuiContainerBackgroundLayer(0.0f, -100, -100); - - GL11.glPopAttrib(); - - if (this.gui.slotcontainer != null) { - GL11.glPushAttrib(GL11.GL_ALL_ATTRIB_BITS); - RenderHelper.enableGUIStandardItemLighting(); - GL11.glEnable(GL12.GL_RESCALE_NORMAL); - GL11.glDisable(GL11.GL_DEPTH_TEST); - List slots = this.gui.slotcontainer.inventorySlots; - - for (Slot slot : slots) { - if (slot != null && slot.getStack() != null) { - GuiContainerManager.drawItem(slot.xDisplayPosition, slot.yDisplayPosition, slot.getStack()); - } - } - - GL11.glPopAttrib(); - } - - this.gui.drawGuiContainerForegroundLayer(-100, -100); - - GL11.glPopMatrix(); - } - } - - protected static class CraftingChainTooltipLineHandler implements ITooltipLineHandler { - - protected int groupId; - protected BookmarkCraftingChain craftingChain; - - protected ItemsTooltipLineHandler inputs; - protected ItemsTooltipLineHandler outputs; - protected ItemsTooltipLineHandler remainder; - - protected Dimension size = new Dimension(); - - public CraftingChainTooltipLineHandler(int groupId, BookmarkCraftingChain craftingChain) { - this.groupId = groupId; - this.craftingChain = craftingChain; - - this.inputs = new ItemsTooltipLineHandler( - translate("bookmark.crafting_chain.input"), - craftingChain.inputs.values().stream().collect(Collectors.toCollection(LinkedList::new)), - true, - Integer.MAX_VALUE); - - this.outputs = new ItemsTooltipLineHandler( - translate("bookmark.crafting_chain.output"), - craftingChain.outputs.values().stream().collect(Collectors.toCollection(LinkedList::new)), - true, - Integer.MAX_VALUE); - - this.remainder = new ItemsTooltipLineHandler( - translate("bookmark.crafting_chain.remainder"), - craftingChain.remainder.values().stream().collect(Collectors.toCollection(LinkedList::new)), - true, - Integer.MAX_VALUE); - - if (!this.inputs.isEmpty() || !this.outputs.isEmpty() || !this.remainder.isEmpty()) { - this.size.height += 2 + fontRenderer.FONT_HEIGHT; - this.size.width = Math.max( - this.inputs.getSize().width, - Math.max(this.outputs.getSize().width, this.remainder.getSize().width)); - - this.size.height += this.inputs.getSize().height + this.outputs.getSize().height - + this.remainder.getSize().height; - } - } - - @Override - public Dimension getSize() { - return this.size; - } - - @Override - public void draw(int x, int y) { - if (this.size.height == 0) return; - - fontRenderer.drawStringWithShadow( - EnumChatFormatting.AQUA + translate("bookmark.crafting_chain") + EnumChatFormatting.RESET, - x, - y + 2, - 0xee555555); - - y += 2 + fontRenderer.FONT_HEIGHT; - - if (NEIClientConfig.craftingChainDir() == 0) { - - if (!this.inputs.isEmpty()) { - this.inputs.draw(x, y); - y += this.inputs.getSize().height; - } - - if (!this.outputs.isEmpty()) { - this.outputs.draw(x, y); - y += this.outputs.getSize().height; - } - - } else { - - if (!this.outputs.isEmpty()) { - this.outputs.draw(x, y); - y += this.outputs.getSize().height; - } - - if (!this.inputs.isEmpty()) { - this.inputs.draw(x, y); - y += this.inputs.getSize().height; - } - } - - if (!this.remainder.isEmpty()) { - this.remainder.draw(x, y); - } - - } - - } + public Button namespacePrev; + public Button namespaceNext; + public Button pullBookmarkedItems; + public Label namespaceLabel; public BookmarkPanel() { - grid = new BookmarkGrid(); - namespaces.add(new BookmarkGrid()); - setNamespace(0); + this.grid = new BookmarkGrid(); + } + + @Override + public void update() { + this.grid = this.storage.getActiveGrid(); } @Override @@ -1421,7 +111,7 @@ public class BookmarkPanel extends PanelWidget { namespaceLabel = new Label("1", true); - namespacePrev = new Button("Prev") { + namespacePrev = new Button("<") { public boolean onButtonPress(boolean rightclick) { @@ -1429,52 +119,48 @@ public class BookmarkPanel extends PanelWidget { return false; } - return prevNamespace(); - } - - @Override - public String getRenderLabel() { - return "<"; + return storage.prevNamespace(); } }; - namespaceNext = new Button("Next") { + namespaceNext = new Button(">") { public boolean onButtonPress(boolean rightclick) { if (inEditingState() || rightclick) { return false; } - return nextNamespace(); + return storage.nextNamespace(); } - @Override - public String getRenderLabel() { - return ">"; - } }; - pullBookmarkedItems = new Button("Pull") { + pullBookmarkedItems = new Button("P") { public boolean onButtonPress(boolean rightclick) { if (rightclick) { return false; } - return pullBookmarkItems(-1, false); + return pullBookmarkItems(BookmarkGrid.DEFAULT_GROUP_ID, NEIClientUtils.shiftKey()); } @Override - public String getRenderLabel() { - return "P"; + public String getButtonTip() { + return NEIClientUtils.translate("bookmark.pullBookmarkedItems.tip"); } }; } + @Override + public BookmarksGridSlot getSlotMouseOver(int mousex, int mousey) { + return this.grid.getSlotMouseOver(mousex, mousey); + } + @Override public String getLabelText() { - if (((BookmarkGrid) grid).getCraftingMode(BookmarkGrid.DEFAULT_GROUP_ID)) { + if (this.grid.isCraftingMode(BookmarkGrid.DEFAULT_GROUP_ID)) { return String.format("§2[§r%d/%d§2]§r", getPage(), Math.max(1, getNumPages())); } @@ -1487,189 +173,184 @@ public class BookmarkPanel extends PanelWidget { || this.groupingItem != null && this.groupingItem.hasEndRow(); } - public void addItem(ItemStack itemStack) { - addItem(itemStack, true); + public boolean addItem(ItemStack stack) { + return addItem(stack, null, BookmarkGrid.DEFAULT_GROUP_ID); } - public void addItem(ItemStack itemStack, boolean saveSize) { - final BookmarkGrid BGrid = (BookmarkGrid) grid; - int idx = BGrid.indexOf(itemStack, true); + public boolean addItem(ItemStack itemStack, RecipeId recipeId, int groupId) { - if (idx != -1) { - BGrid.removeItem(idx); - } + if (itemStack != null && this.grid.indexOf(groupId, itemStack, recipeId, false) == -1) { + final boolean existsRecipe = recipeId != null + && this.grid.existsRecipe(recipeId, BookmarkGrid.DEFAULT_GROUP_ID); + final NBTTagCompound nbTag = StackInfo.itemStackToNBT(itemStack); + long factor = nbTag.getInteger("Count"); - addOrRemoveItem(itemStack, null, null, false, saveSize); - } + if (existsRecipe) { - public void addOrRemoveItem(ItemStack stackA) { - addOrRemoveItem(stackA, null, null, false, false); - } - - public void addOrRemoveItem(ItemStack stackover, final String handlerName, final List ingredients, - boolean saveIngredients, boolean saveSize) { - loadBookmarksIfNeeded(); - - final Point mousePos = getMousePosition(); - final ItemPanelSlot slot = getSlotMouseOver(mousePos.x, mousePos.y); - final BookmarkGrid BGrid = (BookmarkGrid) grid; - - if (slot != null && StackInfo.equalItemAndNBT(slot.item, stackover, true)) { - BGrid.removeRecipe(slot.slotIndex, saveIngredients); - } else { - BookmarkRecipeId recipeId = null; - - if (handlerName != null && !handlerName.isEmpty() && ingredients != null) { - recipeId = new BookmarkRecipeId(handlerName, ingredients); - } - - final int idx = BGrid.indexOf(stackover, recipeId); - - if (idx != -1) { - BGrid.removeRecipe(idx, saveIngredients); - } else if (saveIngredients && handlerName != null && !handlerName.isEmpty() && ingredients != null) { - BookmarkRecipe recipe = new BookmarkRecipe(stackover); - recipe.handlerName = handlerName; - recipe.recipeId = recipeId; - - for (PositionedStack stack : ingredients) { - recipe.ingredients.add(stack.item); + for (int i = 0; i < this.grid.size(); i++) { + if (this.grid.getBookmarkItem(i).equalsRecipe(recipeId, groupId)) { + final long multiplier = this.grid.getBookmarkItem(i).getMultiplier(); + factor *= multiplier; + break; + } } - addRecipe(recipe, saveSize); } else { - final NBTTagCompound nbTag = StackInfo.itemStackToNBT(stackover); - final ItemStack normalized = StackInfo.loadFromNBT(nbTag, saveSize ? nbTag.getInteger("Count") : 0); - final ItemStackMetadata metadata = new ItemStackMetadata( - recipeId, - nbTag, - false, - BookmarkGrid.DEFAULT_GROUP_ID); - - BGrid.addItem(normalized, metadata); - } - } - - fixCountOfNamespaces(); - } - - public void addRecipe(BookmarkRecipe recipe, boolean saveSize) { - addRecipe(recipe, saveSize, BookmarkGrid.DEFAULT_GROUP_ID); - } - - public void addRecipe(BookmarkRecipe recipe, boolean saveSize, int groupId) { - final BookmarkGrid BGrid = (BookmarkGrid) grid; - final BookmarkRecipeId recipeId = recipe.getRecipeId(); - - if (!BGrid.groups.containsKey(groupId)) { - groupId = BookmarkGrid.DEFAULT_GROUP_ID; - } - - if (recipeId != null) { - final Map uniqueIngredients = new LinkedHashMap<>(); - - BGrid.removeRecipe(recipeId, groupId); - - for (ItemStack stack : recipe.ingredients) { - final String GUID = StackInfo.getItemStackGUID(stack); - final NBTTagCompound nbTagU = uniqueIngredients.get(GUID); - final NBTTagCompound nbTagS = StackInfo.itemStackToNBT(stack); - - if (nbTagU == null) { - uniqueIngredients.put(GUID, nbTagS); - } else { - nbTagU.setInteger("Count", nbTagU.getInteger("Count") + nbTagS.getInteger("Count")); - } + factor = nbTag.hasKey("gtFluidName") ? Math.min(144, nbTag.getInteger("Count")) : 1; } - for (String GUID : uniqueIngredients.keySet()) { - final NBTTagCompound nbTag = uniqueIngredients.get(GUID); - final ItemStack normalized = StackInfo.loadFromNBT(nbTag, saveSize ? nbTag.getInteger("Count") : 0); - final ItemStackMetadata metadata = new ItemStackMetadata(recipeId.copy(), nbTag, true, groupId); - - BGrid.addItem(normalized, metadata); - } + this.grid.addItem(BookmarkItem.of(groupId, itemStack, factor, recipeId, false), true); + return true; } - for (ItemStack stack : recipe.result) { - final NBTTagCompound nbTag = StackInfo.itemStackToNBT(stack); - final ItemStack normalized = StackInfo.loadFromNBT(nbTag, saveSize ? nbTag.getInteger("Count") : 0); - final ItemStackMetadata metadata = new ItemStackMetadata(recipeId, nbTag, false, groupId); - - BGrid.addItem(normalized, metadata); - } - - fixCountOfNamespaces(); + return false; } - public void addBookmarkGroup(List items, BookmarkViewMode viewMode) { - List recipes = new ArrayList<>(); + public boolean addRecipe(Recipe recipe, int groupId) { + final RecipeId recipeId = recipe.getRecipeId(); - for (ItemStack stack : items) { - recipes.add(new BookmarkRecipe(stack)); + if (recipe != null && this.grid.getGroup(groupId) != null && !this.grid.existsRecipe(recipeId, groupId)) { + this.grid.addRecipe(recipe, groupId); + return true; } - addBookmarkGroup(recipes, viewMode, false); + return false; } - public void addBookmarkGroup(List recipes, BookmarkViewMode viewMode, boolean crafting) { - if (recipes.isEmpty()) return; - - final BookmarkGrid BGrid = (BookmarkGrid) grid; - int groupId = BookmarkGrid.DEFAULT_GROUP_ID; + public boolean addGroup(List items, BookmarkViewMode viewMode, boolean crafting) { + if (items.isEmpty()) return false; if (viewMode == null) { - viewMode = BGrid.getViewMode(BookmarkGrid.DEFAULT_GROUP_ID); + viewMode = this.grid.getViewMode(BookmarkGrid.DEFAULT_GROUP_ID); } - for (ItemStackMetadata meta : BGrid.metadata) { - if (meta.groupId > groupId) { - groupId = meta.groupId; - } - } + final int groupId = this.grid.addGroup(new BookmarkGroup(viewMode, crafting)); + final ItemStackSet uniqueStack = new ItemStackSet(); + final Set uniqueRecipe = new HashSet<>(); - BGrid.groups.put(++groupId, new BookmarkGroup(viewMode, crafting)); + for (Object item : items) { - for (BookmarkRecipe recipe : recipes) { - addRecipe(recipe, true, groupId); - } + if (item instanceof Recipe recipe) { - fixCountOfNamespaces(); - } - - public BookmarkRecipeId getBookmarkRecipeId(int slotIndex) { - ItemStackMetadata meta = ((BookmarkGrid) grid).getMetadata(slotIndex); - return meta.ingredient ? null : meta.recipeId; - } - - public BookmarkRecipeId getBookmarkRecipeId(ItemStack stackA) { - final BookmarkGrid BGrid = (BookmarkGrid) grid; - BookmarkRecipeId recipeId = null; - - for (int idx = 0; idx < BGrid.realItems.size(); idx++) { - if (StackInfo.equalItemAndNBT(stackA, BGrid.realItems.get(idx), true)) { - if ((recipeId = getBookmarkRecipeId(idx)) != null) { - return recipeId; + if (uniqueRecipe.add(recipe.getRecipeId())) { + this.grid.addRecipe(recipe, groupId); } + + } else if (item instanceof RecipeId recipeId) { + ItemStack stack = recipeId.getResult(); + + if (stack != null && uniqueRecipe.add(recipeId)) { + this.grid.addItem( + BookmarkItem.of(groupId, stack, StackInfo.getAmount(stack), recipeId, false), + true); + uniqueStack.add(stack); + } + + } else { + ItemStack stack = extractItem(item); + + if (stack != null && !uniqueStack.contains(stack)) { + this.grid.addItem(BookmarkItem.of(groupId, stack), true); + uniqueStack.add(stack); + } + } + + } + + return true; + } + + protected static ItemStack extractItem(Object item) { + + if (item instanceof PositionedStack positionedStack) { + return positionedStack.item; + } + + if (item instanceof NBTTagCompound nbTag) { + return StackInfo.loadFromNBT(nbTag); + } + + if (item instanceof RecipeIngredient ingr) { + return ingr.getItemStack(); + } + + if (item instanceof ItemStack stack) { + return stack; } return null; } - public boolean removeBookmarkRecipeId(BookmarkRecipeId recipeId) { - final BookmarkGrid BGrid = (BookmarkGrid) grid; - final int groupId = BookmarkGrid.DEFAULT_GROUP_ID; + public boolean removeItem(ItemStack itemStack, RecipeId recipeId, int groupId) { + final int itemIndex = this.grid.indexOf(groupId, itemStack, recipeId, false); - return BGrid.removeRecipe(recipeId, groupId); + if (itemIndex != -1) { + this.grid.removeRecipe(itemIndex, false); + return true; + } + + return false; + } + + public boolean removeSlot(int mousex, int mousey, boolean removeFullRecipe) { + final BookmarksGridSlot slot = getSlotMouseOver(mousex, mousey); + + if (slot == null) { + return false; + } + + final int groupId = slot.getGroupId(); + final RecipeId recipeId = slot.getRecipeId(); + final BookmarkGroup group = slot.getGroup(); + + if (recipeId == null || !removeFullRecipe) { + this.grid.removeRecipe(slot.itemIndex, removeFullRecipe); + + return true; + } + + if (group.crafting != null && group.crafting.recipeRelations.containsKey(recipeId)) { + boolean removed = false; + + for (RecipeId relRecipeId : group.crafting.recipeRelations.get(recipeId)) { + removed = this.grid.removeRecipe(relRecipeId, groupId) || removed; + } + + return removed; + } + + return this.grid.removeRecipe(recipeId, groupId); + } + + public boolean removeRecipe(RecipeId recipeId, int groupId) { + return this.grid.removeRecipe(recipeId, groupId); + } + + public boolean removeGroup(int groupId) { + + if (this.grid.getGroup(groupId) != null) { + this.grid.removeGroup(groupId); + return true; + } + + return false; + } + + public RecipeId getRecipeId(int itemIndex) { + BookmarkItem meta = this.grid.getCalculatedItem(itemIndex); + return meta.isIngredient ? null : meta.recipeId; + } + + public boolean existsRecipe(RecipeId recipeId, int groupId) { + return this.grid.existsRecipe(recipeId, groupId); } public int getHoveredGroupId(boolean groupPanel) { - final BookmarkGrid BGrid = (BookmarkGrid) grid; - final int overRowIndex = BGrid.getHoveredRowIndex(groupPanel); + final int overRowIndex = this.grid.getHoveredRowIndex(groupPanel); - if (groupPanel && overRowIndex == -1) { - final Point mouse = getMousePosition(); + if (groupPanel && overRowIndex == -1 && !this.grid.isEmpty()) { + final Point mouse = GuiDraw.getMousePosition(); if (new Rectangle4i(pagePrev.x + pagePrev.w, pagePrev.y, pageNext.x - (pagePrev.x + pagePrev.w), pagePrev.h) .contains(mouse.x, mouse.y)) { @@ -1677,94 +358,18 @@ public class BookmarkPanel extends PanelWidget { } } - return overRowIndex >= 0 ? BGrid.getRowGroupId(overRowIndex) : -1; - } - - public void removeGroup(int groupId) { - final BookmarkGrid BGrid = (BookmarkGrid) grid; - - if (groupId >= 0 || groupId < BGrid.groups.size()) { - BGrid.removeGroup(groupId); + if (overRowIndex >= 0) { + int groupId = this.grid.getRowGroupId(overRowIndex); + return groupId == BookmarkGrid.DEFAULT_GROUP_ID ? -1 : groupId; } + + return -1; } protected String getNamespaceLabelText(boolean shortFormat) { - String activePage = String.valueOf(activeNamespaceIndex + 1); + String activePage = String.valueOf(this.storage.getActiveIndex() + 1); - return shortFormat ? activePage : (activePage + "/" + fixCountOfNamespaces()); - } - - protected int fixCountOfNamespaces() { - - if (namespaces.get(getNamespaceSize() - 1).size() > 0) { - namespaces.add(new BookmarkGrid()); - } else if (activeNamespaceIndex == getNamespaceSize() - 2 && grid.size() == 0) { - namespaces.remove(getNamespaceSize() - 1); - } - - return getNamespaceSize(); - } - - protected boolean removeEmptyNamespaces() { - - if (activeNamespaceIndex != getNamespaceSize() - 1 && grid.size() == 0) { - namespaces.remove(activeNamespaceIndex); - setNamespace(activeNamespaceIndex); - return true; - } - - return false; - } - - protected boolean prevNamespace() { - if (bookmarksState != BookmarkLoadingState.LOADED) { - return false; - } - - fixCountOfNamespaces(); - removeEmptyNamespaces(); - - if (activeNamespaceIndex == 0) { - setNamespace(getNamespaceSize() - 1); - } else { - setNamespace(activeNamespaceIndex - 1); - } - - return true; - } - - protected boolean nextNamespace() { - if (bookmarksState != BookmarkLoadingState.LOADED) { - return false; - } - - if (removeEmptyNamespaces()) { - return true; - } - - if (activeNamespaceIndex == fixCountOfNamespaces() - 1) { - setNamespace(0); - } else { - setNamespace(activeNamespaceIndex + 1); - } - - return true; - } - - protected void setNamespace(int namespaceIndex) { - activeNamespaceIndex = Math.min(namespaceIndex, namespaces.size() - 1); - grid = namespaces.get(activeNamespaceIndex); - - if (grid.size() == 0 && activeNamespaceIndex > 0) { - ((BookmarkGrid) grid).setViewMode( - BookmarkGrid.DEFAULT_GROUP_ID, - namespaces.get(activeNamespaceIndex - 1).getViewMode(BookmarkGrid.DEFAULT_GROUP_ID)); - } - - } - - public int getNamespaceSize() { - return namespaces.size(); + return shortFormat ? activePage : (activePage + "/" + this.storage.getNamespaceSize()); } public void load() { @@ -1780,7 +385,7 @@ public class BookmarkPanel extends PanelWidget { dir.mkdirs(); } - bookmarkFile = new File(dir, "bookmarks.ini"); + final File bookmarkFile = new File(dir, "bookmarks.ini"); if (!bookmarkFile.exists()) { final File globalBookmarks = new File(CommonUtils.getMinecraftDir(), "saves/NEI/global/bookmarks.ini"); @@ -1803,218 +408,18 @@ public class BookmarkPanel extends PanelWidget { } } - bookmarksState = null; + this.storage.load(bookmarkFile); } - public void saveBookmarks() { - - if (bookmarkFile == null || bookmarksState != BookmarkLoadingState.LOADED) { - return; - } - - List strings = new ArrayList<>(); - NBTTagCompound navigation = new NBTTagCompound(); - navigation.setInteger("namespaceIndex", activeNamespaceIndex); - - for (int grpIdx = 0; grpIdx < getNamespaceSize() - 1; grpIdx++) { - BookmarkGrid grid = namespaces.get(grpIdx); - JsonObject settings = new JsonObject(); - JsonObject groups = new JsonObject(); - - for (int groupId : grid.groups.keySet()) { - BookmarkGroup group = grid.groups.get(groupId); - JsonObject groupJson = new JsonObject(); - groupJson.add("viewmode", new JsonPrimitive(group.viewMode.toString())); - groupJson.add("crafting", new JsonPrimitive(group.crafting != null)); - groups.add(String.valueOf(groupId), groupJson); - } - - settings.add("groups", groups); - strings.add("; " + NBTJson.toJson(settings)); - - navigation.setInteger("namespacePage." + grpIdx, grid.page); - - for (int idx = 0; idx < grid.size(); idx++) { - - try { - final NBTTagCompound nbTag = StackInfo.itemStackToNBT(grid.realItems.get(idx)); - - if (nbTag != null) { - JsonObject row = new JsonObject(); - ItemStackMetadata meta = grid.metadata.get(idx); - - row.add("item", NBTJson.toJsonObject(nbTag)); - row.add("factor", new JsonPrimitive(meta.factor)); - row.add("ingredient", new JsonPrimitive(meta.ingredient)); - - if (meta.groupId != BookmarkGrid.DEFAULT_GROUP_ID) { - row.add("groupId", new JsonPrimitive(meta.groupId)); - } - - if (meta.recipeId != null) { - row.add("recipeId", meta.recipeId.toJsonObject()); - } - - strings.add(NBTJson.toJson(row)); - } - - } catch (JsonSyntaxException e) { - NEIClientConfig.logger.error("Failed to stringify bookmarked ItemStack to json string"); - } - } - } - - try (FileOutputStream output = new FileOutputStream(bookmarkFile)) { - IOUtils.writeLines(strings, "\n", output, StandardCharsets.UTF_8); - NEIClientConfig.world.nbt.setTag("bookmark", navigation); - } catch (IOException e) { - NEIClientConfig.logger.error("Filed to save bookmarks list to file {}", bookmarkFile, e); - } - } - - protected void loadBookmarksIfNeeded() { - - if (bookmarksState != null || bookmarksState == BookmarkLoadingState.LOADING) { - return; - } - - bookmarksState = BookmarkLoadingState.LOADING; - - if (bookmarkFile == null || !bookmarkFile.exists()) { - bookmarksState = BookmarkLoadingState.LOADED; - return; - } - - List itemStrings; - try (FileInputStream reader = new FileInputStream(bookmarkFile)) { - NEIClientConfig.logger.info("Loading bookmarks from file {}", bookmarkFile); - itemStrings = IOUtils.readLines(reader, StandardCharsets.UTF_8); - } catch (IOException e) { - NEIClientConfig.logger.error("Failed to load bookmarks from file {}", bookmarkFile, e); - return; - } - - final JsonParser parser = new JsonParser(); - final List namespaces = new ArrayList<>(); - NBTTagCompound navigation = new NBTTagCompound(); - namespaces.add(new BookmarkGrid()); - BookmarkGrid grid = namespaces.get(0); - int groupId = BookmarkGrid.DEFAULT_GROUP_ID; - - if (NEIClientConfig.world.nbt.hasKey("bookmark")) { - navigation = NEIClientConfig.world.nbt.getCompoundTag("bookmark"); - } - - for (String itemStr : itemStrings) { - - try { - - if (itemStr.isEmpty()) { - itemStr = "; {}"; - } - - if (itemStr.startsWith("; ")) { - JsonObject settings = parser.parse(itemStr.substring(2)).getAsJsonObject(); - - if (grid.size() > 0) { - // do not create empty namespaces - grid = new BookmarkGrid(); - namespaces.add(grid); - } - - if (navigation.hasKey("namespacePage." + (namespaces.size() - 1))) { - grid.page = navigation.getInteger("namespacePage." + (namespaces.size() - 1)); - } - - if (settings.get("viewmode") != null) { - grid.groups.get(BookmarkGrid.DEFAULT_GROUP_ID).viewMode = BookmarkViewMode - .valueOf(settings.get("viewmode").getAsString()); - } else if (settings.get("groups") != null && settings.get("groups") instanceof JsonObject) { - final JsonObject jsonObject = (JsonObject) settings.get("groups"); - BookmarkGroup group; - JsonObject value; - - for (Map.Entry jsonEntry : jsonObject.entrySet()) { - if (jsonEntry.getValue() instanceof JsonObject) { - value = (JsonObject) jsonEntry.getValue(); - group = new BookmarkGroup( - value.has("viewmode") - ? BookmarkViewMode.valueOf(value.get("viewmode").getAsString()) - : BookmarkViewMode.DEFAULT, - value.has("crafting") ? value.get("crafting").getAsBoolean() : false); - grid.groups.put(Integer.valueOf(jsonEntry.getKey()), group); - } - } - } - - continue; - } - - JsonObject jsonObject = parser.parse(itemStr).getAsJsonObject(); - BookmarkRecipeId recipeId = null; - NBTTagCompound itemStackNBT; - - if (jsonObject.get("item") != null) { - itemStackNBT = (NBTTagCompound) NBTJson.toNbt(jsonObject.get("item")); - } else { // old format - itemStackNBT = (NBTTagCompound) NBTJson.toNbt(jsonObject); - } - - if (jsonObject.get("recipeId") != null && jsonObject.get("recipeId") instanceof JsonObject) { - recipeId = new BookmarkRecipeId((JsonObject) jsonObject.get("recipeId")); - } - - ItemStack itemStack = StackInfo.loadFromNBT(itemStackNBT); - - if (itemStack != null) { - groupId = jsonObject.has("groupId") ? jsonObject.get("groupId").getAsInt() - : BookmarkGrid.DEFAULT_GROUP_ID; - grid.realItems.add(itemStack); - grid.metadata.add( - new ItemStackMetadata( - recipeId, - jsonObject.has("factor") ? Math.abs(jsonObject.get("factor").getAsInt()) - : (itemStackNBT.hasKey("gtFluidName") ? 144 : 1), - jsonObject.has("ingredient") ? jsonObject.get("ingredient").getAsBoolean() : false, - grid.groups.containsKey(groupId) ? groupId : BookmarkGrid.DEFAULT_GROUP_ID, - itemStackNBT.hasKey("gtFluidName"))); - } else { - NEIClientConfig.logger.warn( - "Failed to load bookmarked ItemStack from json string, the item no longer exists:\n{}", - itemStr); - } - - } catch (IllegalArgumentException | JsonSyntaxException | IllegalStateException e) { - NEIClientConfig.logger.error("Failed to load bookmarked ItemStack from json string:\n{}", itemStr); - } - } - - for (BookmarkGrid gr : namespaces) { - gr.onItemsChanged(); - } - - this.namespaces = namespaces; - bookmarksState = BookmarkLoadingState.LOADED; - - if (navigation.hasKey("namespaceIndex")) { - setNamespace(navigation.getInteger("namespaceIndex")); - } else { - setNamespace(0); - } - - } - - @Override - public void resize(GuiContainer gui) { - loadBookmarksIfNeeded(); - super.resize(gui); + public void save() { + this.storage.save(); } @Override protected int resizeHeader(GuiContainer gui) { final LayoutStyleMinecraft layout = (LayoutStyleMinecraft) LayoutManager.getLayoutStyle(); final int rows = (int) Math.ceil((double) layout.buttonCount / layout.numButtons); - final int diff = rows * BookmarkGrid.SLOT_SIZE + getMarginTop(gui) - y; + final int diff = rows * 19 + getMarginTop(gui) - y; if (diff > 0) { y += diff; @@ -2032,7 +437,7 @@ public class BookmarkPanel extends PanelWidget { final int leftBorder = y + h > button.y ? button.x + button.w + 2 : x; final int rightBorder = x + w; final int center = leftBorder + Math.max(0, (rightBorder - leftBorder) / 2); - int labelWidth = 2; + int labelWidth; namespacePrev.h = namespaceNext.h = pullBookmarkedItems.h = BUTTON_SIZE; namespacePrev.w = namespaceNext.w = pullBookmarkedItems.w = BUTTON_SIZE; @@ -2060,11 +465,13 @@ public class BookmarkPanel extends PanelWidget { public void setVisible() { super.setVisible(); - if (grid.getPerPage() > 0 && getNamespaceSize() > 1) { + if (grid.getPerPage() > 0 && !this.storage.isEmpty()) { LayoutManager.addWidget(namespacePrev); LayoutManager.addWidget(namespaceNext); LayoutManager.addWidget(namespaceLabel); - if (BookmarkContainerInfo.getBookmarkContainerHandler(getGuiContainer()) != null) { + + if (!grid.isEmpty() + && BookmarkContainerInfo.getBookmarkContainerHandler(NEIClientUtils.getGuiContainer()) != null) { LayoutManager.addWidget(pullBookmarkedItems); } } @@ -2090,191 +497,52 @@ public class BookmarkPanel extends PanelWidget { return gui.height - getMarginTop(gui) - PADDING; } - protected ItemStack getDraggedStackWithQuantity(int mouseDownSlot) { - final ItemStack stack = grid.getItem(mouseDownSlot); - - if (stack == null) { - return null; - } - - final ItemStackMetadata meta = ((BookmarkGrid) grid).getMetadata(mouseDownSlot); - int amount = stack.stackSize; - - if (amount == 0 && !meta.fluidDisplay) { - amount = NEIClientConfig.showItemQuantityWidget() ? NEIClientConfig.getItemQuantity() : 0; - - if (amount == 0) { - amount = stack.getMaxStackSize(); - } - } - - return NEIServerUtils.copyStack(stack, amount); + protected ItemStack getDraggedStackWithQuantity(ItemStack itemStack) { + return ItemQuantityField.prepareStackWithQuantity(itemStack, StackInfo.getAmount(itemStack)); } @Override public void mouseDragged(int mousex, int mousey, int button, long heldTime) { - if (mouseDownSlot >= grid.realItems.size()) { - mouseDownSlot = -1; + if (this.grid.getSlotBySlotIndex(this.mouseDownSlot) == null) { + this.mouseDownSlot = -1; } if (this.groupingItem != null) { - final int overRowIndex = (mousey - grid.marginTop) / BookmarkGrid.SLOT_SIZE; + final int overRowIndex = grid.getRowIndex(mousey); - if (this.groupingItem.hasEndRow() || overRowIndex != this.groupingItem.startRowIndex || heldTime > 250) { - this.groupingItem.setEndRowIndex( - (BookmarkGrid) grid, - Math.max(0, Math.min(overRowIndex, grid.getLastRowIndex()))); + if (this.groupingItem.hasEndRow() || overRowIndex != this.groupingItem.getStartSlotIndex() + || heldTime > 250) { + this.groupingItem.setEndPoint(Math.max(0, Math.min(overRowIndex, grid.getLastRowIndex()))); } return; } else if (this.sortableGroup != null) { - final BookmarkGrid BGrid = (BookmarkGrid) grid; - final int overRowIndex = (mousey - grid.marginTop) / BookmarkGrid.SLOT_SIZE; + final int overRowIndex = grid.getRowIndex(mousey); - if (this.sortableGroup.groupId != BGrid.getRowGroupId(overRowIndex)) { - BGrid.moveGroup(this.sortableGroup.groupId, overRowIndex); + if (overRowIndex >= 0 && this.sortableGroup.groupId != this.grid.getRowGroupId(overRowIndex)) { + this.grid.moveGroup(this.sortableGroup, overRowIndex); } return; } else if (this.sortableItem != null) { - final BookmarkGrid BGrid = (BookmarkGrid) grid; - final ItemStackMetadata sortMeta = this.sortableItem.metadata.get(0); - final BookmarkViewMode sortViewMode = BGrid.getViewMode(sortMeta.groupId); - ItemPanelSlot mouseOverSlot = getSlotMouseOver(mousex, mousey); - if (sortViewMode == BookmarkViewMode.TODO_LIST && !sortMeta.ingredient) { - mouseOverSlot = getSlotMouseOver(grid.marginLeft + grid.paddingLeft, mousey); + if (!this.sortableItem.isEmpty()) { + this.sortableItem.mouseDragged(mousex, mousey); + } - if (mouseOverSlot != null) { - float ySlot = (float) (mousey - BGrid.marginTop) / BookmarkGrid.SLOT_SIZE; - int lastRowIndex = BGrid.getLastRowIndex(); - int overRowIndex = (int) ySlot; - int beforeGroupId = overRowIndex > 0 ? BGrid.getRowGroupId(overRowIndex - 1) - : BookmarkGrid.DEFAULT_GROUP_ID; - int afterGroupId = overRowIndex < lastRowIndex ? BGrid.getRowGroupId(overRowIndex + 1) - : BookmarkGrid.DEFAULT_GROUP_ID; - int overGroupId = BGrid.metadata.get(mouseOverSlot.slotIndex).groupId; - ySlot -= overRowIndex; - - if (this.sortableItem.items.indexOf(BGrid.realItems.get(mouseOverSlot.slotIndex)) == -1 - && overGroupId == sortMeta.groupId) { - - if (mouseOverSlot.slotIndex < BGrid.realItems.indexOf(sortableItem.items.get(0))) { - BGrid.moveItem(this.sortableItem, mouseOverSlot.slotIndex, overGroupId, true); - } else { - BGrid.moveItem( - this.sortableItem, - BGrid.getRowItemIndex(overRowIndex, false), - overGroupId, - false); - } - - } else if (ySlot <= 0.25) { - - if (BGrid.getViewMode(beforeGroupId) == BookmarkViewMode.TODO_LIST - && !existsRecipeIdInGroupId(sortMeta.recipeId, beforeGroupId)) { - BGrid.moveItem(this.sortableItem, mouseOverSlot.slotIndex, beforeGroupId, true); - } - - } else if (ySlot > 0.25 && ySlot <= 0.5) { - - if (beforeGroupId != afterGroupId && beforeGroupId != BookmarkGrid.DEFAULT_GROUP_ID - && (afterGroupId != BookmarkGrid.DEFAULT_GROUP_ID - || BGrid.getViewMode(overGroupId) == BookmarkViewMode.DEFAULT) - && BGrid.getViewMode(BookmarkGrid.DEFAULT_GROUP_ID) == BookmarkViewMode.TODO_LIST - && !existsRecipeIdInGroupId(sortMeta.recipeId, BookmarkGrid.DEFAULT_GROUP_ID)) { - beforeGroupId = BookmarkGrid.DEFAULT_GROUP_ID; - } - - if (BGrid.getViewMode(beforeGroupId) == BookmarkViewMode.TODO_LIST - && !existsRecipeIdInGroupId(sortMeta.recipeId, beforeGroupId)) { - BGrid.moveItem(this.sortableItem, mouseOverSlot.slotIndex, beforeGroupId, true); - } - - } else if (ySlot > 0.5 && ySlot < 0.75) { - - if (beforeGroupId != afterGroupId - && (beforeGroupId != BookmarkGrid.DEFAULT_GROUP_ID - || BGrid.getViewMode(overGroupId) == BookmarkViewMode.DEFAULT) - && afterGroupId != BookmarkGrid.DEFAULT_GROUP_ID - && BGrid.getViewMode(BookmarkGrid.DEFAULT_GROUP_ID) == BookmarkViewMode.TODO_LIST - && !existsRecipeIdInGroupId(sortMeta.recipeId, BookmarkGrid.DEFAULT_GROUP_ID)) { - afterGroupId = BookmarkGrid.DEFAULT_GROUP_ID; - } - - if (BGrid.getViewMode(afterGroupId) == BookmarkViewMode.TODO_LIST - && !existsRecipeIdInGroupId(sortMeta.recipeId, afterGroupId)) { - BGrid.moveItem( - this.sortableItem, - BGrid.getRowItemIndex(overRowIndex, false), - afterGroupId, - false); - } - - } else if (ySlot >= 0.75) { - - if (BGrid.getViewMode(afterGroupId) == BookmarkViewMode.TODO_LIST - && !existsRecipeIdInGroupId(sortMeta.recipeId, afterGroupId)) { - BGrid.moveItem( - this.sortableItem, - BGrid.getRowItemIndex(overRowIndex, false), - afterGroupId, - false); - } - - } - - } - - } else if (mouseOverSlot != null - && this.sortableItem.items.indexOf(BGrid.realItems.get(mouseOverSlot.slotIndex)) == -1) { - final ItemStackMetadata meta = BGrid.getMetadata(mouseOverSlot.slotIndex); - - if (meta.groupId == sortMeta.groupId) { - - if (sortViewMode == BookmarkViewMode.DEFAULT) { - BGrid.moveItem(this.sortableItem, mouseOverSlot.slotIndex); - } else if (sortViewMode == BookmarkViewMode.TODO_LIST && meta.recipeId != null - && meta.recipeId.equals(sortMeta.recipeId)) { - BGrid.moveItem(this.sortableItem, mouseOverSlot.slotIndex); - } - - } - - } return; } - if (button == 0 && NEIClientUtils.shiftKey() && mouseDownSlot >= 0 && this.sortableItem == null) { - ItemPanelSlot mouseOverSlot = getSlotMouseOver(mousex, mousey); + if (button == 0 && NEIClientUtils.shiftKey() && this.mouseDownSlot >= 0 && this.sortableItem == null) { + final BookmarksGridSlot mouseOverSlot = getSlotMouseOver(mousex, mousey); - if (mouseOverSlot == null || mouseOverSlot.slotIndex != mouseDownSlot || heldTime > 250) { - final BookmarkGrid BGrid = (BookmarkGrid) grid; - final ItemStackMetadata meta = BGrid.getMetadata(mouseDownSlot); - final List items = new ArrayList<>(); - final List metadata = new ArrayList<>(); - - if (meta.recipeId == null || meta.ingredient - || BGrid.getViewMode(meta.groupId) == BookmarkViewMode.DEFAULT) { - items.add(BGrid.realItems.get(this.mouseDownSlot)); - metadata.add(BGrid.metadata.get(this.mouseDownSlot)); - } else { - - for (int i = 0; i < BGrid.metadata.size(); i++) { - if (BGrid.metadata.get(i).recipeId != null && meta.groupId == BGrid.metadata.get(i).groupId - && meta.recipeId.equals(BGrid.metadata.get(i).recipeId)) { - items.add(BGrid.realItems.get(i)); - metadata.add(BGrid.metadata.get(i)); - } - } + if (mouseOverSlot == null || mouseOverSlot.slotIndex != this.mouseDownSlot || heldTime > 250) { + final BookmarksGridSlot slot = this.grid.getSlotBySlotIndex(this.mouseDownSlot); + if (slot != null && slot.itemIndex >= 0) { + this.sortableItem = new SortableItem(this.grid, slot.getBookmarkItem()); + this.grid.onGridChanged(); } - - this.sortableItem = new SortableItem(items, metadata); - this.sortableItem.shiftX = -8; - this.sortableItem.shiftY = -8; - - grid.onGridChanged(); } return; @@ -2283,29 +551,20 @@ public class BookmarkPanel extends PanelWidget { super.mouseDragged(mousex, mousey, button, heldTime); } - private boolean existsRecipeIdInGroupId(BookmarkRecipeId recipeId, int groupId) { - if (recipeId == null) return false; - - for (ItemStackMetadata meta : ((BookmarkGrid) grid).metadata) { - if (meta.equalsRecipe(recipeId, groupId) && this.sortableItem.metadata.indexOf(meta) == -1) { - return true; - } - } - - return false; - } - private int getNextSlot() { - final List mask = grid.getMask(); - final int columns = grid.getColumns(); - final int perPage = grid.getRows() * columns; - final boolean line = ((BookmarkGrid) grid).getViewMode(BookmarkGrid.DEFAULT_GROUP_ID) - == BookmarkViewMode.TODO_LIST; + final int columns = this.grid.getColumns(); + final int perPage = this.grid.getRows() * columns; + final boolean line = this.grid.getViewMode(BookmarkGrid.DEFAULT_GROUP_ID) == BookmarkViewMode.TODO_LIST; + final List mask = this.grid.getMask(); + int slotIndex = mask.isEmpty() ? 0 : mask.get(mask.size() - 1).slotIndex + 1; - for (int i = mask.size(); i < perPage; i++) { - if (!grid.isInvalidSlot(i) && (!line || (i % columns) == 0)) { - return i; + while (slotIndex < perPage) { + + if ((!line || (slotIndex % columns) == 0) && !grid.isInvalidSlot(slotIndex)) { + return slotIndex; } + + slotIndex++; } return -1; @@ -2315,96 +574,19 @@ public class BookmarkPanel extends PanelWidget { public void postDraw(int mousex, int mousey) { if (this.sortableItem != null) { - final BookmarkGrid BGrid = (BookmarkGrid) grid; - final List mask = BGrid.getMask(); - Point startRect = null; - - GuiContainerManager.drawItems.zLevel += 100; - - for (int i = 0; i < mask.size(); i++) { - if (mask.get(i) != null) { - int idx = mask.get(i); - - if (this.sortableItem.items.contains(BGrid.realItems.get(idx))) { - Rectangle4i rect = grid.getSlotRect(i); - - if (startRect == null) { - startRect = new Point(rect.x - this.sortableItem.shiftX, rect.y - this.sortableItem.shiftY); - } - - GuiContainerManager.drawItem( - mousex + rect.x - startRect.x + 1, - mousey + rect.y - startRect.y + 1, - BGrid.getItem(idx), - true, - ""); - } - } - } - - GuiContainerManager.drawItems.zLevel -= 100; + this.sortableItem.postDraw(mousex, mousey); } if (this.sortableGroup != null) { - final BookmarkGrid BGrid = (BookmarkGrid) grid; - final List mask = BGrid.getMask(); - Point startRect = null; - - GuiContainerManager.drawItems.zLevel += 100; - - for (int i = 0; i < mask.size(); i++) { - if (mask.get(i) != null) { - int idx = mask.get(i); - - if (BGrid.metadata.get(idx).groupId == this.sortableGroup.groupId) { - Rectangle4i rect = grid.getSlotRect(i); - - if (startRect == null) { - startRect = new Point( - rect.x - this.sortableGroup.shiftX, - rect.y - this.sortableGroup.shiftY); - } - - GuiContainerManager.drawItem( - mousex + rect.x - startRect.x + 1, - mousey + rect.y - startRect.y + 1, - BGrid.getItem(idx), - true, - ""); - } - } - } - - if (startRect != null) { - int startRowIndex = -1; - int endRowIndex = 0; - - for (int rowIndex = 0; rowIndex < BGrid.gridGroupMask.size(); rowIndex++) { - if (BGrid.gridGroupMask.get(rowIndex) == this.sortableGroup.groupId) { - - if (startRowIndex == -1) { - startRowIndex = rowIndex; - } - - endRowIndex = rowIndex; - } - } - - GL11.glPushMatrix(); - GL11.glTranslated(mousex - startRect.x, mousey - startRect.y, 1); - BGrid.drawGroup(this.sortableGroup.groupId, startRowIndex, endRowIndex); - GL11.glPopMatrix(); - } - - GuiContainerManager.drawItems.zLevel -= 100; + this.sortableGroup.postDraw(mousex, mousey); } - if (ItemPanels.itemPanel.draggedStack != null && this.contains(mousex, mousey)) { - final int idx = getNextSlot(); + if (ItemPanels.itemPanel.draggedStack != null && contains(mousex, mousey)) { + final int slotIndex = getNextSlot(); - if (idx >= 0) { - Rectangle4i rect = grid.getSlotRect(idx); - drawRect(rect.x, rect.y, rect.w, rect.h, 0xee555555); // highlight + if (slotIndex >= 0) { + Rectangle4i rect = this.grid.getSlotRect(slotIndex); + NEIClientUtils.drawRect(rect.x, rect.y, rect.w, rect.h, ItemsGrid.HIGHLIGHT_COLOR); } } @@ -2415,19 +597,19 @@ public class BookmarkPanel extends PanelWidget { public boolean handleClickExt(int mousex, int mousey, int button) { if (button == 0 && NEIClientUtils.shiftKey()) { - final BookmarkGrid BGrid = (BookmarkGrid) grid; - final int overRowIndex = BGrid.getHoveredRowIndex(true); - final int groupId = BGrid.getRowGroupId(overRowIndex); + final int overRowIndex = this.grid.getHoveredRowIndex(true); + final int groupId = overRowIndex != -1 ? this.grid.getRowGroupId(overRowIndex) + : BookmarkGrid.DEFAULT_GROUP_ID; if (groupId != BookmarkGrid.DEFAULT_GROUP_ID) { int rowIndex = overRowIndex; - while (rowIndex >= 0 && groupId == BGrid.getRowGroupId(rowIndex)) { + while (rowIndex >= 0 && groupId == this.grid.getRowGroupId(rowIndex)) { rowIndex--; } - Rectangle4i rect = BGrid.getSlotRect(rowIndex + 1, 0); - this.sortableGroup = new SortableGroup(groupId); + Rectangle4i rect = this.grid.getSlotRect(rowIndex + 1, 0); + this.sortableGroup = new SortableGroup(this.grid, groupId); this.sortableGroup.shiftX = rect.x - mousex; this.sortableGroup.shiftY = rect.y - mousey; @@ -2436,22 +618,40 @@ public class BookmarkPanel extends PanelWidget { } if (!NEIClientUtils.shiftKey() && (button == 0 || button == 1)) { - final int overRowIndex = ((BookmarkGrid) grid).getHoveredRowIndex(true); + final int overRowIndex = this.grid.getHoveredRowIndex(true); if (overRowIndex != -1) { - this.groupingItem = new GroupingItem((BookmarkGrid) grid, button == 1, overRowIndex); + int groupId = this.grid.getRowGroupId(overRowIndex); + + if (button == 1) { + groupId = BookmarkGrid.DEFAULT_GROUP_ID; + } else if (groupId == BookmarkGrid.DEFAULT_GROUP_ID) { + groupId = Integer.MIN_VALUE; + } + + this.groupingItem = new GroupingItem(this.grid, groupId, overRowIndex); + + if (this.groupingItem.startPoint.topRowIndex == -1 + || this.groupingItem.startPoint.bottomRowIndex == -1) { + this.groupingItem = null; + } + return true; } } if (new Rectangle4i(pagePrev.x + pagePrev.w, pagePrev.y, pageNext.x - (pagePrev.x + pagePrev.w), pagePrev.h) .contains(mousex, mousey)) { - final BookmarkGrid BGrid = (BookmarkGrid) grid; - if (button == 0) { - BGrid.toggleViewMode(BookmarkGrid.DEFAULT_GROUP_ID); + if (NEIClientUtils.altKey() && button == 0) { + this.grid.toggleCollapsedState(BookmarkGrid.DEFAULT_GROUP_ID); + NEIClientUtils.playClickSound(); + } else if (button == 0) { + this.grid.toggleViewMode(BookmarkGrid.DEFAULT_GROUP_ID); + NEIClientUtils.playClickSound(); } else if (button == 1) { - BGrid.toggleCraftingMode(BookmarkGrid.DEFAULT_GROUP_ID); + this.grid.toggleCraftingMode(BookmarkGrid.DEFAULT_GROUP_ID); + NEIClientUtils.playClickSound(); } return true; @@ -2462,124 +662,204 @@ public class BookmarkPanel extends PanelWidget { @Override public List handleTooltip(GuiContainer gui, int mousex, int mousey, List currenttip) { - final BookmarkGrid BGrid = (BookmarkGrid) grid; - final int overRowIndex = BGrid.getHoveredRowIndex(true); - if (new Rectangle4i(pagePrev.x + pagePrev.w, pagePrev.y, pageNext.x - (pagePrev.x + pagePrev.w), pagePrev.h) - .contains(mousex, mousey)) { - currenttip.add(translate("bookmark.group")); - currenttip = craftingChainTooltip(BookmarkGrid.DEFAULT_GROUP_ID, currenttip); + if (this.grid.getPerPage() == 0 || this.grid.isEmpty()) { + return new ArrayList<>(); } - if (new Rectangle4i(pullBookmarkedItems.x, pullBookmarkedItems.y, pullBookmarkedItems.w, pullBookmarkedItems.h) - .contains(mousex, mousey) && BookmarkContainerInfo.getBookmarkContainerHandler(gui) != null) { - currenttip.add(translate("bookmark.pullBookmarkedItems.tip")); - } + final int overRowIndex = this.grid.getHoveredRowIndex(true); if (overRowIndex != -1) { - final int groupId = BGrid.getRowGroupId(overRowIndex); + final int groupId = this.grid.getRowGroupId(overRowIndex); - currenttip.add(translate("bookmark.group")); + currenttip.add(NEIClientUtils.translate("bookmark.group")); if (groupId != BookmarkGrid.DEFAULT_GROUP_ID) { - currenttip = craftingChainTooltip(groupId, currenttip); + currenttip = recipeChainTooltip(groupId, currenttip); } else { - this.craftingChainTooltipLineHandler = null; + this.recipeChainTooltipLineHandler = null; } - } else { - this.craftingChainTooltipLineHandler = null; - } + } else + if (new Rectangle4i(pagePrev.x + pagePrev.w, pagePrev.y, pageNext.x - (pagePrev.x + pagePrev.w), pagePrev.h) + .contains(mousex, mousey)) { + currenttip.add(NEIClientUtils.translate("bookmark.group")); + currenttip = recipeChainTooltip(BookmarkGrid.DEFAULT_GROUP_ID, currenttip); + } else { + this.recipeChainTooltipLineHandler = null; + } return super.handleTooltip(gui, mousex, mousey, currenttip); } + @Override + public boolean handleClick(int mousex, int mousey, int button) { + final BookmarksGridSlot hoverSlot = getSlotMouseOver(mousex, mousey); + + if (hoverSlot != null && button == 0 && NEIClientUtils.altKey()) { + final BookmarkGroup group = hoverSlot.getGroup(); + + if (group.collapsed) { + group.collapsed = false; + NEIClientUtils.playClickSound(); + grid.onItemsChanged(); + } else if (group.crafting != null && hoverSlot.getRecipeId() != null) { + grid.toggleCollapsedRecipe(hoverSlot.getGroupId(), hoverSlot.getRecipeId()); + NEIClientUtils.playClickSound(); + } + + return true; + } + + return super.handleClick(mousex, mousey, button); + } + @Override public Map handleHotkeys(GuiContainer gui, int mousex, int mousey, Map hotkeys) { - final BookmarkGrid BGrid = (BookmarkGrid) grid; - final int overRowIndex = BGrid.getHoveredRowIndex(true); + final int overRowIndex = this.grid.getHoveredRowIndex(true); + final BookmarksGridSlot slot = getSlotMouseOver(mousex, mousey); + + if (this.grid.getPerPage() == 0 || this.grid.isEmpty()) { + return hotkeys; + } if (new Rectangle4i(pagePrev.x + pagePrev.w, pagePrev.y, pageNext.x - (pagePrev.x + pagePrev.w), pagePrev.h) .contains(mousex, mousey)) { - hotkeys.put(translate("bookmark.group.toggle_mode.key"), translate("bookmark.group.toggle_mode")); hotkeys.put( - translate("bookmark.group.toggle_crafting_chain.key"), - translate("bookmark.group.toggle_crafting_chain")); + NEIClientUtils.getKeyName( + NEIClientUtils.CTRL_HASH, + NEIMouseUtils.MOUSE_BTN_NONE + NEIMouseUtils.MOUSE_SCROLL), + NEIClientUtils.translate("bookmark.change_quantity")); - String keyName = NEIClientConfig.getKeyName("gui.remove_recipe"); - if (keyName != null) { - hotkeys.put(keyName, translate("bookmark.group.remove_recipe")); - } + hotkeys.put( + NEIClientUtils.getKeyName( + NEIClientUtils.CTRL_HASH + NEIClientUtils.ALT_HASH, + NEIMouseUtils.MOUSE_BTN_NONE + NEIMouseUtils.MOUSE_SCROLL), + NEIClientUtils.translate("bookmark.change_quantity_step")); + + hotkeys.put( + NEIMouseUtils.getKeyName(NEIMouseUtils.MOUSE_BTN_LMB), + NEIClientUtils.translate("bookmark.group.toggle_mode")); + hotkeys.put( + NEIMouseUtils.getKeyName(NEIMouseUtils.MOUSE_BTN_RMB), + NEIClientUtils.translate("bookmark.group.toggle_crafting_chain")); + hotkeys.put( + NEIClientConfig.getKeyName("gui.remove_recipe"), + NEIClientUtils.translate("bookmark.group.remove_recipe")); if (BookmarkContainerInfo.getBookmarkContainerHandler(gui) != null) { - - keyName = NEIClientConfig.getKeyName("gui.bookmark_pull_items"); - if (keyName != null) { - hotkeys.put(keyName, translate("bookmark.group.pull_items")); - } - - keyName = NEIClientConfig.getKeyName("gui.bookmark_pull_items_ingredients"); - if (keyName != null) { - hotkeys.put(keyName, translate("bookmark.group.pull_items_ingredients")); - } + hotkeys.put( + NEIClientConfig.getKeyName("gui.bookmark_pull_items"), + NEIClientUtils.translate("bookmark.group.pull_items")); + hotkeys.put( + NEIClientConfig.getKeyName("gui.bookmark_pull_items", NEIClientUtils.SHIFT_HASH), + NEIClientUtils.translate("bookmark.group.pull_items_shift")); } + hotkeys.put( + NEIClientUtils.getKeyName(NEIClientUtils.ALT_HASH, NEIMouseUtils.MOUSE_BTN_LMB), + NEIClientUtils.translate("bookmark.group.toggle_collapse")); } if (overRowIndex != -1) { - final int groupId = BGrid.getRowGroupId(overRowIndex); + final int groupId = this.grid.getRowGroupId(overRowIndex); - hotkeys.put(translate("bookmark.group.include_group.key"), translate("bookmark.group.include_group")); + hotkeys.put( + NEIMouseUtils.getKeyName(NEIMouseUtils.MOUSE_BTN_LMB + NEIMouseUtils.MOUSE_DRAG), + NEIClientUtils.translate("bookmark.group.include_group")); if (groupId != BookmarkGrid.DEFAULT_GROUP_ID) { - hotkeys.put(translate("bookmark.group.exclude_group.key"), translate("bookmark.group.exclude_group")); - hotkeys.put(translate("bookmark.group.toggle_mode.key"), translate("bookmark.group.toggle_mode")); hotkeys.put( - translate("bookmark.group.toggle_crafting_chain.key"), - translate("bookmark.group.toggle_crafting_chain")); - hotkeys.put(translate("bookmark.group.sorting.key"), translate("bookmark.group.sorting")); + NEIClientUtils.getKeyName( + NEIClientUtils.CTRL_HASH, + NEIMouseUtils.MOUSE_BTN_NONE + NEIMouseUtils.MOUSE_SCROLL), + NEIClientUtils.translate("bookmark.change_quantity")); + + hotkeys.put( + NEIClientUtils.getKeyName( + NEIClientUtils.CTRL_HASH + NEIClientUtils.ALT_HASH, + NEIMouseUtils.MOUSE_BTN_NONE + NEIMouseUtils.MOUSE_SCROLL), + NEIClientUtils.translate("bookmark.change_quantity_step")); + + hotkeys.put( + NEIMouseUtils.getKeyName(NEIMouseUtils.MOUSE_BTN_RMB + NEIMouseUtils.MOUSE_DRAG), + NEIClientUtils.translate("bookmark.group.exclude_group")); + hotkeys.put( + NEIMouseUtils.getKeyName(NEIMouseUtils.MOUSE_BTN_LMB), + NEIClientUtils.translate("bookmark.group.toggle_mode")); + hotkeys.put( + NEIMouseUtils.getKeyName(NEIMouseUtils.MOUSE_BTN_RMB), + NEIClientUtils.translate("bookmark.group.toggle_crafting_chain")); + + hotkeys.put( + NEIClientUtils.getKeyName( + NEIClientUtils.SHIFT_HASH, + NEIMouseUtils.MOUSE_BTN_LMB + NEIMouseUtils.MOUSE_DRAG), + NEIClientUtils.translate("bookmark.group.sorting")); + + hotkeys.put( + NEIClientUtils.getKeyName(NEIClientUtils.ALT_HASH, NEIMouseUtils.MOUSE_BTN_LMB), + NEIClientUtils.translate("bookmark.group.toggle_collapse")); + } + } + + if (slot != null) { + final BookmarkGroup group = slot.getGroup(); + + hotkeys.put( + NEIClientUtils.getKeyName( + NEIClientUtils.CTRL_HASH, + NEIMouseUtils.MOUSE_BTN_NONE + NEIMouseUtils.MOUSE_SCROLL), + NEIClientUtils.translate("bookmark.change_quantity")); + + hotkeys.put( + NEIClientUtils.getKeyName( + NEIClientUtils.CTRL_HASH + NEIClientUtils.ALT_HASH, + NEIMouseUtils.MOUSE_BTN_NONE + NEIMouseUtils.MOUSE_SCROLL), + NEIClientUtils.translate("bookmark.change_quantity_step")); + + hotkeys.put( + NEIClientUtils.getKeyName( + NEIClientUtils.SHIFT_HASH, + NEIMouseUtils.MOUSE_BTN_LMB + NEIMouseUtils.MOUSE_DRAG), + NEIClientUtils.translate("bookmark.item.sorting")); + + if (group.collapsed) { + hotkeys.put( + NEIClientUtils.getKeyName(NEIClientUtils.ALT_HASH, NEIMouseUtils.MOUSE_BTN_LMB), + NEIClientUtils.translate("bookmark.group.toggle_collapse")); + } else if (group.crafting != null) { + hotkeys.put( + NEIClientUtils.getKeyName(NEIClientUtils.ALT_HASH, NEIMouseUtils.MOUSE_BTN_LMB), + NEIClientUtils.translate("bookmark.recipe.toggle_collapse")); } - String keyName = NEIClientConfig.getKeyName("gui.remove_recipe"); - if (keyName != null) { - hotkeys.put(keyName, translate("bookmark.group.remove_recipe")); - } - - if (BookmarkContainerInfo.getBookmarkContainerHandler(gui) != null) { - keyName = NEIClientConfig.getKeyName("gui.bookmark_pull_items"); - - if (keyName != null) { - hotkeys.put(keyName, translate("bookmark.group.pull_items")); - } - - keyName = NEIClientConfig.getKeyName("gui.bookmark_pull_items_ingredients"); - if (keyName != null) { - hotkeys.put(keyName, translate("bookmark.group.pull_items_ingredients")); - } + if (this.acceptsFollowingTooltipLineHandler != null) { + hotkeys.put( + NEIClientUtils.getKeyName( + NEIClientUtils.SHIFT_HASH, + NEIMouseUtils.MOUSE_BTN_NONE + NEIMouseUtils.MOUSE_SCROLL), + NEIClientUtils.translate("recipe.accepts.scroll")); } } return hotkeys; } - private List craftingChainTooltip(int groupId, List currenttip) { - final BookmarkGrid BGrid = (BookmarkGrid) grid; - final BookmarkGroup group = BGrid.groups.get(groupId); + private List recipeChainTooltip(int groupId, List currenttip) { + boolean crafting = this.grid.getGroup(groupId).crafting != null; - if (this.craftingChainTooltipLineHandler == null || this.craftingChainTooltipLineHandler.groupId != groupId) { - - if (group.crafting != null) { - this.craftingChainTooltipLineHandler = new CraftingChainTooltipLineHandler(groupId, group.crafting); - } else { - this.craftingChainTooltipLineHandler = null; - } - } else if (group.crafting == null) { - this.craftingChainTooltipLineHandler = null; + if (this.recipeChainTooltipLineHandler == null || this.recipeChainTooltipLineHandler.groupId != groupId + || this.recipeChainTooltipLineHandler.crafting != crafting) { + this.recipeChainTooltipLineHandler = new RecipeChainTooltipLineHandler( + groupId, + crafting, + this.grid.createRecipeChainMath(groupId)); } - if (this.craftingChainTooltipLineHandler != null) { - currenttip.add(GuiDraw.TOOLTIP_HANDLER + GuiDraw.getTipLineId(this.craftingChainTooltipLineHandler)); + if (this.recipeChainTooltipLineHandler != null) { + currenttip.add(GuiDraw.TOOLTIP_HANDLER + GuiDraw.getTipLineId(this.recipeChainTooltipLineHandler)); } return currenttip; @@ -2593,7 +873,7 @@ public class BookmarkPanel extends PanelWidget { return true; } - if (((BookmarkGrid) grid).getHoveredRowIndex(true) != -1) { + if (this.grid.getHoveredRowIndex(true) != -1) { return true; } @@ -2603,9 +883,34 @@ public class BookmarkPanel extends PanelWidget { @Override public List handleItemTooltip(GuiContainer gui, ItemStack itemstack, int mousex, int mousey, List currenttip) { + final BookmarksGridSlot slot = this.grid.getSlotMouseOver(mousex, mousey); - if (contains(mousex, mousey) && itemstack != null && this.recipeTooltipLineHandler != null) { - currenttip.add(GuiDraw.TOOLTIP_HANDLER + GuiDraw.getTipLineId(this.recipeTooltipLineHandler)); + if (slot != null) { + + if (slot.isIngredient() && slot.getRecipeId() != null && slot.getBookmarkItem().permutations.size() > 1) { + RecipeId recipeId = slot.getRecipeId(); + + if (recipeId == null) { + this.acceptsFollowingTooltipLineHandler = null; + } else if (this.acceptsFollowingTooltipLineHandler == null + || ((int) this.acceptsFollowingTooltipLineHandler.tooltipGUID) != slot.itemIndex) { + this.acceptsFollowingTooltipLineHandler = AcceptsFollowingTooltipLineHandler.of( + slot.itemIndex, + new ArrayList<>(slot.getBookmarkItem().permutations.values()), + slot.getItemStack(), + 2); + } + + if (this.acceptsFollowingTooltipLineHandler != null) { + currenttip.add( + GuiDraw.TOOLTIP_HANDLER + GuiDraw.getTipLineId(this.acceptsFollowingTooltipLineHandler)); + } + } else { + this.acceptsFollowingTooltipLineHandler = null; + } + + } else { + this.acceptsFollowingTooltipLineHandler = null; } return currenttip; @@ -2615,37 +920,38 @@ public class BookmarkPanel extends PanelWidget { public void mouseUp(int mousex, int mousey, int button) { if (this.sortableItem != null) { - this.sortableItem = null; - this.mouseDownSlot = -1; - grid.onItemsChanged(); /* make sure grid redraws the new item */ + grid.onItemsChanged(); } else if (this.sortableGroup != null) { - this.sortableGroup = null; - this.mouseDownSlot = -1; - grid.onItemsChanged(); /* make sure grid redraws the new item */ - } else if (this.groupingItem != null) { - final BookmarkGrid BGrid = (BookmarkGrid) grid; + grid.onItemsChanged(); + } else if (this.groupingItem != null && this.groupingItem.hasEndRow()) { + this.grid.createGroup(this.groupingItem); + this.grid.onItemsChanged(); + } else { + int rowIndex = this.grid.getHoveredRowIndex(true); + int groupId = rowIndex != -1 ? this.grid.getRowGroupId(rowIndex) : BookmarkGrid.DEFAULT_GROUP_ID; - if (this.groupingItem.hasEndRow()) { - BGrid.createGroup(this.groupingItem); - } else { - int groupId = BGrid.getRowGroupId(this.groupingItem.startRowIndex); - - if (groupId != BookmarkGrid.DEFAULT_GROUP_ID) { - if (button == 0) { - BGrid.toggleViewMode(groupId); - } else if (button == 1) { - BGrid.toggleCraftingMode(groupId); - } + if (groupId != BookmarkGrid.DEFAULT_GROUP_ID) { + if (button == 0 && NEIClientUtils.altKey()) { + this.grid.toggleCollapsedState(groupId); + NEIClientUtils.playClickSound(); + } else if (button == 0) { + this.grid.toggleViewMode(groupId); + NEIClientUtils.playClickSound(); + } else if (button == 1) { + this.grid.toggleCraftingMode(groupId); + NEIClientUtils.playClickSound(); } + this.grid.onItemsChanged(); + } else { + super.mouseUp(mousex, mousey, button); } - - this.mouseDownSlot = -1; - this.groupingItem = null; - grid.onItemsChanged(); /* make sure grid redraws the new item */ - } else { - super.mouseUp(mousex, mousey, button); } + + this.sortableItem = null; + this.sortableGroup = null; + this.groupingItem = null; + this.mouseDownSlot = -1; } @Override @@ -2658,9 +964,9 @@ public class BookmarkPanel extends PanelWidget { namespacePrev.h).contains(mousex, mousey)) { if (shift > 0) { - prevNamespace(); + this.storage.prevNamespace(); } else { - nextNamespace(); + this.storage.nextNamespace(); } return true; @@ -2671,54 +977,78 @@ public class BookmarkPanel extends PanelWidget { } if (!inEditingState() && NEIClientUtils.controlKey()) { - final ItemPanelSlot slot = getSlotMouseOver(mousex, mousey); + final BookmarksGridSlot slot = this.grid.getSlotMouseOver(mousex, mousey); if (slot != null) { - final BookmarkGrid BGrid = (BookmarkGrid) grid; - final ItemStackMetadata overMeta = BGrid.getMetadata(slot.slotIndex); - final HashMap items = new HashMap<>(); - int shiftMultiplier = 1; + int multiplier = 1; if (NEIClientUtils.altKey()) { - shiftMultiplier = NEIClientConfig.showItemQuantityWidget() ? NEIClientConfig.getItemQuantity() : 0; - if (shiftMultiplier == 0) { - shiftMultiplier = slot.item.getMaxStackSize(); + multiplier = NEIClientConfig.showItemQuantityWidget() ? NEIClientConfig.getItemQuantity() : 0; + if (multiplier == 0) { + multiplier = slot.getItemStack().getMaxStackSize(); } } - if (NEIClientUtils.shiftKey()) { + this.grid.shiftItemAmount(slot.itemIndex, multiplier * shift); + return true; + } else { + final int groupId = getHoveredGroupId(true); - for (int slotIndex = grid.size() - 1; slotIndex >= 0; slotIndex--) { - if (overMeta.equalsRecipe(BGrid.getMetadata(slotIndex)) && slotIndex != slot.slotIndex) { - items.put(slotIndex, shiftStackSize(BGrid, slotIndex, shift, shiftMultiplier)); + if (groupId != -1) { + int multiplier = 1; + + if (NEIClientUtils.altKey()) { + multiplier = NEIClientConfig.showItemQuantityWidget() ? NEIClientConfig.getItemQuantity() : 0; + if (multiplier == 0) { + multiplier = 64; } } + + this.grid.shiftGroupAmount(groupId, multiplier * shift); + return true; } - - items.put(slot.slotIndex, shiftStackSize(BGrid, slot.slotIndex, shift, shiftMultiplier)); - - for (int slotIndex : items.keySet()) { - if (items.get(slotIndex) != null) { - BGrid.realItems.set(slotIndex, items.get(slotIndex)); - } - } - - BGrid.onItemsChanged(); - return true; } } - if (super.onMouseWheel(shift, mousex, mousey)) { - final int overRowIndex = (mousey - grid.marginTop) / BookmarkGrid.SLOT_SIZE; - final BookmarkGrid BGrid = (BookmarkGrid) grid; + if (!inEditingState() && NEIClientUtils.shiftKey() + && this.grid.contains(mousex, mousey) + && this.acceptsFollowingTooltipLineHandler != null) { + int itemIndex = (int) this.acceptsFollowingTooltipLineHandler.tooltipGUID; + final BookmarkItem item = this.grid.getCalculatedItem(itemIndex); - if (this.sortableGroup != null && this.sortableGroup.groupId != BGrid.getRowGroupId(overRowIndex)) { - BGrid.moveGroup(this.sortableGroup.groupId, overRowIndex); + if (item.permutations.size() > 1) { + final List items = acceptsFollowingTooltipLineHandler.items; + ItemStack activeStack = this.acceptsFollowingTooltipLineHandler.getActiveStack(); + int stackIndex = 0; + + for (int i = 0; i < items.size(); i++) { + if (StackInfo.equalItemAndNBT(activeStack, items.get(i), true)) { + stackIndex = i; + break; + } + } + + activeStack = items.get((items.size() - shift + stackIndex) % items.size()); + + this.acceptsFollowingTooltipLineHandler.setActiveStack(activeStack); + this.grid.getBookmarkItem(Math.abs(itemIndex)).itemStack = activeStack; + this.grid.onItemsChanged(); + } + + return true; + } + + if (super.onMouseWheel(shift, mousex, mousey)) { + final int overRowIndex = grid.getRowIndex(mousey); + + if (this.sortableGroup != null && overRowIndex >= 0 + && this.sortableGroup.groupId != this.grid.getRowGroupId(overRowIndex)) { + this.grid.moveGroup(this.sortableGroup, overRowIndex); } if (this.groupingItem != null - && (this.groupingItem.hasEndRow() || overRowIndex != this.groupingItem.startRowIndex)) { - this.groupingItem.setEndRowIndex(BGrid, Math.max(0, Math.min(overRowIndex, grid.getLastRowIndex()))); + && (this.groupingItem.hasEndRow() || overRowIndex != this.groupingItem.getStartSlotIndex())) { + this.groupingItem.setEndPoint(Math.max(0, Math.min(overRowIndex, grid.getLastRowIndex()))); } return true; @@ -2727,68 +1057,179 @@ public class BookmarkPanel extends PanelWidget { return false; } - private ItemStack shiftStackSize(BookmarkGrid BGrid, int slotIndex, int shift, int shiftMultiplier) { - final NBTTagCompound nbTag = StackInfo.itemStackToNBT(BGrid.getItem(slotIndex)); - final ItemStackMetadata meta = BGrid.getMetadata(slotIndex); + public boolean pullBookmarkItems(int groupId, boolean shift) { + return pullBookmarkItems(this.grid.createRecipeChainMath(groupId), shift); + } - if (meta.factor > 0) { - final int multiplier = nbTag.getInteger("Count") / meta.factor; - final long count = ((long) (multiplier + shift * shiftMultiplier) / shiftMultiplier) * shiftMultiplier - * meta.factor; + public boolean pullBookmarkItems(RecipeChainMath math, boolean shift) { + final GuiContainer guiContainer = NEIClientUtils.getGuiContainer(); + final IBookmarkContainerHandler containerHandler = BookmarkContainerInfo + .getBookmarkContainerHandler(guiContainer); - if (count <= Integer.MAX_VALUE) { - return StackInfo.loadFromNBT(nbTag, Math.max(count, 0)); + if (containerHandler == null || math == null) { + return false; + } + + final ItemStackAmount pullItemStacks = new ItemStackAmount(); + final InventoryPlayer playerInventory = guiContainer.mc.thePlayer.inventory; + final ItemStackAmount inventory = shift ? ItemStackAmount.of(Arrays.asList(playerInventory.mainInventory)) + : new ItemStackAmount(); + + if (math.outputRecipes.isEmpty()) { + + for (BookmarkItem item : math.initialItems) { + final long invStackSize = inventory.getOrDefault(item.itemStack, 0L); + final long amount = item.amount - invStackSize * item.fluidCellAmount; + if (amount > 0) { + pullItemStacks.add(item.getItemStack(amount)); + } + } + + } else { + final ItemStackAmount containerStorage = ItemStackAmount + .of(containerHandler.getStorageStacks(guiContainer)); + final List initialItems = new ArrayList<>(); + + for (BookmarkItem item : math.initialItems) { + final long invStackSize = inventory.getOrDefault(item.itemStack, 0L); + final long strStackSize = containerStorage.getOrDefault(item.itemStack, 0L); + final long amount = item.amount - invStackSize * item.fluidCellAmount + - strStackSize * item.fluidCellAmount; + + if (amount > 0) { + initialItems.add(item.copyWithAmount(amount)); + } + } + + math.initialItems.clear(); + math.initialItems.addAll(initialItems); + + for (ItemStack stack : inventory.values()) { + math.initialItems.add(BookmarkItem.of(-1, stack)); + } + + for (ItemStack stack : containerStorage.values()) { + math.initialItems.add(BookmarkItem.of(-2, stack)); + } + + math.refresh(); + + for (BookmarkItem item : math.initialItems) { + if (item.groupId == -2 && math.requiredAmount.getOrDefault(item, -1L) > 0) { + pullItemStacks.add(item.getItemStack(math.requiredAmount.get(item))); + } + } + + } + + if (pullItemStacks.isEmpty()) { + return false; + } + + ArrayList items = new ArrayList<>(); + int freeSlots = 0; + + for (ItemStack stack : playerInventory.mainInventory) { + if (stack == null || stack.stackSize < stack.getMaxStackSize() && pullItemStacks.contains(stack)) { + freeSlots++; + } + } + + if (freeSlots == 0) { + return false; + } + + for (ItemStack stack : pullItemStacks.values()) { + stack.stackSize = Math.min(stack.stackSize, freeSlots * stack.getMaxStackSize()); + if (stack.stackSize > 0) { + items.add(stack); + } + } + + if (!items.isEmpty()) { + containerHandler.pullBookmarkItemsFromContainer(guiContainer, items); + } + + return true; + } + + @Deprecated + public void addItem(ItemStack stack, boolean saveSize) { + addItem(saveSize ? stack : StackInfo.withAmount(stack, 0)); + } + + @Deprecated + public void addOrRemoveItem(ItemStack stack) { + if (!removeItem(stack, null, BookmarkGrid.DEFAULT_GROUP_ID)) { + addItem(stack); + } + } + + @Deprecated + public void addOrRemoveItem(ItemStack stack, String handlerName, List ingredients, + boolean saveIngredients, boolean saveSize) { + + if (!removeItem(stack, null, BookmarkGrid.DEFAULT_GROUP_ID)) { + if (saveIngredients && handlerName != null + && !handlerName.isEmpty() + && ingredients != null + && !ingredients.isEmpty()) { + addRecipe(Recipe.of(Arrays.asList(stack), handlerName, ingredients), BookmarkGrid.DEFAULT_GROUP_ID); + } else { + addItem(stack, saveSize); + } + } + } + + @Deprecated + public void addRecipe(BookmarkRecipe recipe, boolean saveSize) { + addRecipe(recipe, saveSize, BookmarkGrid.DEFAULT_GROUP_ID); + } + + @Deprecated + public void addRecipe(BookmarkRecipe recipe, boolean saveSize, int groupId) { + addRecipe(recipe.getRecipe(), groupId); + } + + @Deprecated + public void addBookmarkGroup(List items, BookmarkViewMode viewMode) { + addGroup(items, viewMode, false); + } + + @Deprecated + public void addBookmarkGroup(List recipes, BookmarkViewMode viewMode, boolean crafting) { + List items = new ArrayList<>(); + + for (BookmarkRecipe recipe : recipes) { + items.add(recipe.getRecipe()); + } + + addGroup(items, viewMode, crafting); + } + + @Deprecated + public BookmarkRecipeId getBookmarkRecipeId(int itemIndex) { + BookmarkItem item = this.grid.getCalculatedItem(itemIndex); + return item == null || item.isIngredient || item.recipeId == null ? null + : new BookmarkRecipeId(item.recipeId.getHandleName(), item.recipeId.getIngredients()); + } + + @Deprecated + public BookmarkRecipeId getBookmarkRecipeId(ItemStack stackA) { + BookmarkRecipeId recipeId = null; + + for (int itemIndex = 0; itemIndex < this.grid.size(); itemIndex++) { + if (StackInfo.equalItemAndNBT(stackA, this.grid.getBookmarkItem(itemIndex).itemStack, true) + && (recipeId = getBookmarkRecipeId(itemIndex)) != null) { + return recipeId; } } return null; } - public boolean pullBookmarkItems(int groupId, boolean onlyIngredients) { - final GuiContainer guiContainer = getGuiContainer(); - final IBookmarkContainerHandler containerHandler = BookmarkContainerInfo - .getBookmarkContainerHandler(guiContainer); - - if (containerHandler == null) { - return false; - } - - final BookmarkGrid BGrid = (BookmarkGrid) grid; - final ArrayList items = new ArrayList<>(); - final ItemStackMap uniqueItems = new ItemStackMap<>(); - ItemStackMetadata meta; - - for (int idx = 0; idx < BGrid.realItems.size(); idx++) { - meta = BGrid.metadata.get(idx); - - if (groupId == -1 || groupId == meta.groupId) { - final ItemStack stack = BGrid.getItem(idx); - final BookmarkGroup group = BGrid.groups.get(meta.groupId); - - if (!onlyIngredients || meta.ingredient && (group == null || group.crafting == null - || group.crafting.inputs.containsKey(BGrid.realItems.get(idx)))) { - uniqueItems.put( - stack, - uniqueItems.getOrDefault(stack, 0L) + StackInfo.itemStackToNBT(stack).getInteger("Count")); - } - - } - } - - for (ItemStackMap.Entry entry : uniqueItems.entries()) { - if (entry.value > 0) { - items.add( - StackInfo.loadFromNBT( - StackInfo.itemStackToNBT(entry.key), - Math.min(entry.value, 9 * 4 * entry.key.getMaxStackSize()))); - } - } - - if (items.isEmpty()) { - return false; - } - - containerHandler.pullBookmarkItemsFromContainer(guiContainer, items); - return true; + @Deprecated + public boolean removeBookmarkRecipeId(BookmarkRecipeId recipeId) { + return false; } } diff --git a/src/main/java/codechicken/nei/CollapsibleItems.java b/src/main/java/codechicken/nei/CollapsibleItems.java index 2046764b..9274dc48 100644 --- a/src/main/java/codechicken/nei/CollapsibleItems.java +++ b/src/main/java/codechicken/nei/CollapsibleItems.java @@ -150,10 +150,16 @@ public class CollapsibleItems { } public static void putItem(ItemStack stack) { - final GroupItem group = CollapsibleItems.groups.stream().filter(g -> g.matches(stack)).findFirst().orElse(null); + int groupIndex = -1; - if (group != null) { - CollapsibleItems.cache.put(stack, CollapsibleItems.groups.indexOf(group)); + for (int i = 0; i < CollapsibleItems.groups.size() && groupIndex == -1; i++) { + if (CollapsibleItems.groups.get(i).matches(stack)) { + groupIndex = i; + } + } + + if (groupIndex != -1) { + CollapsibleItems.cache.put(stack, groupIndex); } } diff --git a/src/main/java/codechicken/nei/FavoriteRecipes.java b/src/main/java/codechicken/nei/FavoriteRecipes.java new file mode 100644 index 00000000..3d8bb40d --- /dev/null +++ b/src/main/java/codechicken/nei/FavoriteRecipes.java @@ -0,0 +1,228 @@ +package codechicken.nei; + +import java.io.File; +import java.io.FileInputStream; +import java.io.FileOutputStream; +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.nio.charset.StandardCharsets; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Optional; + +import net.minecraft.item.ItemStack; +import net.minecraft.nbt.NBTTagCompound; +import net.minecraftforge.fluids.FluidRegistry; +import net.minecraftforge.fluids.FluidStack; + +import org.apache.commons.io.IOUtils; + +import com.google.gson.JsonObject; +import com.google.gson.JsonParser; +import com.google.gson.JsonSyntaxException; + +import codechicken.core.CommonUtils; +import codechicken.nei.api.API; +import codechicken.nei.recipe.Recipe.RecipeId; +import codechicken.nei.recipe.StackInfo; +import codechicken.nei.util.NBTJson; + +public class FavoriteRecipes { + + private static final Map items = new HashMap<>(); + private static final Map fluids = new HashMap<>(); + private static File favoriteFile; + + static { + API.addSubset("Favorites", FavoriteRecipes::contains); + } + + private FavoriteRecipes() {} + + public static void load() { + String worldPath = "global"; + + if (NEIClientConfig.getBooleanSetting("inventory.favorites.worldSpecific")) { + worldPath = NEIClientConfig.getWorldPath(); + } + + final File dir = new File(CommonUtils.getMinecraftDir(), "saves/NEI/" + worldPath); + + if (!dir.exists()) { + dir.mkdirs(); + } + + favoriteFile = new File(dir, "favorites.ini"); + + if (!favoriteFile.exists()) { + final File globalFavorites = new File(CommonUtils.getMinecraftDir(), "saves/NEI/global/favorites.ini"); + final File configFavorites = new File(NEIClientConfig.configDir, "favorites.ini"); + final File defaultFavorites = configFavorites.exists() ? configFavorites : globalFavorites; + + if (defaultFavorites.exists()) { + + try { + if (favoriteFile.createNewFile()) { + InputStream src = new FileInputStream(defaultFavorites); + OutputStream dst = new FileOutputStream(favoriteFile); + + IOUtils.copy(src, dst); + + src.close(); + dst.close(); + } + } catch (IOException e) {} + } + } + + loadData(); + } + + private static void loadData() { + + if (favoriteFile == null || !favoriteFile.exists()) { + return; + } + + List itemStrings; + + try (FileInputStream reader = new FileInputStream(favoriteFile)) { + NEIClientConfig.logger.info("Loading favorites from file {}", favoriteFile); + itemStrings = IOUtils.readLines(reader, StandardCharsets.UTF_8); + } catch (IOException e) { + NEIClientConfig.logger.error("Failed to load favorites from file {}", favoriteFile, e); + return; + } + + final JsonParser parser = new JsonParser(); + items.clear(); + + for (String itemStr : itemStrings) { + + if (itemStr.isEmpty() || itemStr.startsWith("#")) { + continue; + } + + try { + JsonObject jsonObject = parser.parse(itemStr).getAsJsonObject(); + + if (jsonObject.get("item") != null && jsonObject.get("recipeId") != null) { + final NBTTagCompound itemStackNBT = (NBTTagCompound) NBTJson.toNbt(jsonObject.get("item")); + final RecipeId recipeId = RecipeId.of(jsonObject.getAsJsonObject("recipeId")); + final ItemStack stack = StackInfo.loadFromNBT(itemStackNBT); + + if (stack != null) { + final String fluidKey = getFluidKey(stack); + + if (fluidKey != null) { + fluids.put(fluidKey, recipeId); + } + } + + items.put(itemStackNBT, recipeId); + } + + } catch (IllegalArgumentException | JsonSyntaxException | IllegalStateException e) { + NEIClientConfig.logger.error("Failed to load favorite from json string:\n{}", itemStr); + } + + } + + SubsetWidget.updateHiddenItems(); + } + + public static RecipeId getFavorite(ItemStack stack) { + + if (NEIClientConfig.favoritesEnabled() && stack != null) { + RecipeId recipeId = items.get(StackInfo.itemStackToNBT(stack, false)); + + if (recipeId == null) { + recipeId = fluids.get(getFluidKey(stack)); + } + + return recipeId; + } + + return null; + } + + public static ItemStack getFavorite(RecipeId recipeId) { + if (NEIClientConfig.favoritesEnabled()) { + final Optional> result = items.entrySet().stream() + .filter(entry -> entry.getValue().equals(recipeId)).findAny(); + + if (result.isPresent()) { + return StackInfo.loadFromNBT(result.get().getKey()); + } + } + + return null; + } + + public static boolean contains(ItemStack stack) { + return getFavorite(stack) != null; + } + + public static void setFavorite(ItemStack stack, RecipeId recipeId) { + final NBTTagCompound itemStackNBT = StackInfo.itemStackToNBT(stack, false); + final String fluidKey = getFluidKey(stack); + + items.entrySet().removeIf(entry -> entry.getKey().equals(itemStackNBT) || entry.getValue().equals(recipeId)); + fluids.entrySet().removeIf(entry -> entry.getKey().equals(fluidKey) || entry.getValue().equals(recipeId)); + + if (recipeId != null) { + items.put(itemStackNBT, recipeId); + + if (fluidKey != null) { + fluids.put(fluidKey, recipeId); + } + } + + SubsetWidget.updateHiddenItems(); + ItemList.updateFilter.restart(); + } + + private static String getFluidKey(ItemStack stack) { + final FluidStack fluid = StackInfo.getFluid(stack); + + if (fluid != null) { + return FluidRegistry.getFluidName(fluid); + } + + return null; + } + + public static void save() { + + if (favoriteFile == null) { + return; + } + + final List strings = new ArrayList<>(); + + for (Map.Entry entry : items.entrySet()) { + + try { + final JsonObject line = new JsonObject(); + + line.add("item", NBTJson.toJsonObject(entry.getKey())); + line.add("recipeId", entry.getValue().toJsonObject()); + + strings.add(NBTJson.toJson(line)); + } catch (JsonSyntaxException e) { + NEIClientConfig.logger.error("Failed to stringify favorites ItemStack to json string"); + } + + } + + try (FileOutputStream output = new FileOutputStream(favoriteFile)) { + IOUtils.writeLines(strings, "\n", output, StandardCharsets.UTF_8); + } catch (IOException e) { + NEIClientConfig.logger.error("Filed to save favorites list to file {}", favoriteFile, e); + } + } + +} diff --git a/src/main/java/codechicken/nei/HeldItemHandler.java b/src/main/java/codechicken/nei/HeldItemHandler.java index c7d91712..c11f2a51 100644 --- a/src/main/java/codechicken/nei/HeldItemHandler.java +++ b/src/main/java/codechicken/nei/HeldItemHandler.java @@ -5,12 +5,12 @@ import static codechicken.nei.NEIClientUtils.translate; import java.util.List; import net.minecraft.client.gui.inventory.GuiContainer; -import net.minecraft.item.ItemStack; import net.minecraft.util.EnumChatFormatting; import codechicken.nei.guihook.GuiContainerManager; import codechicken.nei.guihook.IContainerInputHandler; import codechicken.nei.guihook.IContainerTooltipHandler; +import codechicken.nei.recipe.AutoCraftingManager; public class HeldItemHandler implements IContainerInputHandler, IContainerTooltipHandler { @@ -25,7 +25,8 @@ public class HeldItemHandler implements IContainerInputHandler, IContainerToolti } protected boolean contains(int mousex, int mousey) { - return NEIClientUtils.getHeldItem() != null + return NEIClientUtils.getHeldItem() != null && !ItemPanels.bookmarkPanel.inEditingState() + && !AutoCraftingManager.processing() && (ItemPanels.bookmarkPanel.contains(mousex, mousey) || ItemPanels.itemPanel.contains(mousex, mousey) || ItemPanels.itemPanel.historyPanel.contains(mousex, mousey)); } @@ -94,17 +95,6 @@ public class HeldItemHandler implements IContainerInputHandler, IContainerToolti } - @Override - public List handleItemDisplayName(GuiContainer gui, ItemStack itemstack, List currenttip) { - return currenttip; - } - - @Override - public List handleItemTooltip(GuiContainer gui, ItemStack itemstack, int mousex, int mousey, - List currenttip) { - return currenttip; - } - @Override public List handleTooltip(GuiContainer gui, int mousex, int mousey, List currenttip) { diff --git a/src/main/java/codechicken/nei/ItemHistoryPanel.java b/src/main/java/codechicken/nei/ItemHistoryPanel.java index 81f2ce96..2c0261fb 100644 --- a/src/main/java/codechicken/nei/ItemHistoryPanel.java +++ b/src/main/java/codechicken/nei/ItemHistoryPanel.java @@ -1,25 +1,64 @@ package codechicken.nei; +import java.util.ArrayList; +import java.util.List; + import net.minecraft.client.gui.inventory.GuiContainer; import net.minecraft.item.ItemStack; import org.lwjgl.opengl.GL11; import codechicken.lib.gui.GuiDraw; -import codechicken.nei.ItemPanel.ItemPanelSlot; +import codechicken.nei.ItemsGrid.ItemsGridSlot; +import codechicken.nei.ItemsGrid.MouseContext; +import codechicken.nei.guihook.IContainerTooltipHandler; import codechicken.nei.recipe.GuiCraftingRecipe; import codechicken.nei.recipe.GuiRecipe; import codechicken.nei.recipe.GuiUsageRecipe; import codechicken.nei.recipe.StackInfo; -public class ItemHistoryPanel extends Widget { +public class ItemHistoryPanel extends Widget implements IContainerTooltipHandler { - protected int mouseDownSlot = -1; - protected ItemsGrid grid; + protected int mouseDownItemIndex = -1; - public ItemHistoryPanel() { - grid = new ItemsGrid(); - } + protected ItemsGrid grid = new ItemsGrid<>() { + + protected List gridMask; + + @Override + protected void onGridChanged() { + this.gridMask = null; + super.onGridChanged(); + } + + @Override + public List getMask() { + + if (this.gridMask == null) { + this.gridMask = new ArrayList<>(); + for (int slotIndex = 0; slotIndex < Math.min(size(), this.rows * this.columns); slotIndex++) { + this.gridMask.add(new ItemsGridSlot(slotIndex, slotIndex, getItem(slotIndex))); + } + } + + return this.gridMask; + } + + @Override + protected MouseContext getMouseContext(int mousex, int mousey) { + final ItemsGridSlot hovered = getSlotMouseOver(mousex, mousey); + + if (hovered != null) { + return new MouseContext( + hovered.slotIndex, + hovered.slotIndex / this.columns, + hovered.slotIndex % this.columns); + } + + return null; + } + + }; public boolean isEmpty() { return grid.isEmpty(); @@ -33,40 +72,40 @@ public class ItemHistoryPanel extends Widget { drawSplittingArea(x, y, w, h, NEIClientConfig.getSetting("inventory.history.historyColor").getHexValue()); } - grid.draw(mousex, mousey); + this.grid.draw(mousex, mousey); } public void update() { - grid.update(); + this.grid.update(); } public void addItem(ItemStack stack) { if (stack != null) { ItemStack is = StackInfo.loadFromNBT(StackInfo.itemStackToNBT(stack), 0); - grid.realItems.removeIf(historyStack -> StackInfo.equalItemAndNBT(historyStack, stack, true)); - grid.realItems.add(0, is); + this.grid.realItems.removeIf(historyStack -> StackInfo.equalItemAndNBT(historyStack, stack, true)); + this.grid.realItems.add(0, is); - if (grid.realItems.size() > Math.max(50, grid.rows * grid.columns)) { - grid.realItems.remove(grid.realItems.size() - 1); + if (this.grid.realItems.size() > Math.max(50, this.grid.rows * this.grid.columns)) { + this.grid.realItems.remove(this.grid.realItems.size() - 1); } - grid.onItemsChanged(); + this.grid.onItemsChanged(); } } public void resize(GuiContainer gui) { - grid.setGridSize(x, y, w, h); - grid.refresh(gui); + this.grid.setGridSize(x, y + 4, w, h - 8); + this.grid.refresh(gui); } @Override public ItemStack getStackMouseOver(int mousex, int mousey) { - ItemPanelSlot slot = getSlotMouseOver(mousex, mousey); - return slot == null ? null : slot.item; + final ItemsGridSlot slot = getSlotMouseOver(mousex, mousey); + return slot == null ? null : slot.getItemStack(); } - public ItemPanelSlot getSlotMouseOver(int mousex, int mousey) { + public ItemsGridSlot getSlotMouseOver(int mousex, int mousey) { return grid.getSlotMouseOver(mousex, mousey); } @@ -107,15 +146,16 @@ public class ItemHistoryPanel extends Widget { @Override public void mouseDragged(int mousex, int mousey, int button, long heldTime) { - if (mouseDownSlot >= 0 && ItemPanels.itemPanel.draggedStack == null + if (this.mouseDownItemIndex >= 0 && ItemPanels.itemPanel.draggedStack == null && NEIClientUtils.getHeldItem() == null && NEIClientConfig.hasSMPCounterPart()) { - ItemPanelSlot mouseOverSlot = getSlotMouseOver(mousex, mousey); + final ItemsGridSlot mouseOverSlot = getSlotMouseOver(mousex, mousey); - if (mouseOverSlot == null || mouseOverSlot.slotIndex != mouseDownSlot || heldTime > 500) { - ItemPanels.itemPanel.draggedStack = getDraggedStackWithQuantity(mouseDownSlot); - mouseDownSlot = -1; + if (mouseOverSlot == null || mouseOverSlot.itemIndex != this.mouseDownItemIndex || heldTime > 500) { + ItemPanels.itemPanel.draggedStack = getDraggedStackWithQuantity( + this.grid.getItem(this.mouseDownItemIndex)); + this.mouseDownItemIndex = -1; } } @@ -125,17 +165,13 @@ public class ItemHistoryPanel extends Widget { public boolean handleClick(int mousex, int mousey, int button) { if (handleClickExt(mousex, mousey, button)) return true; - ItemPanelSlot hoverSlot = getSlotMouseOver(mousex, mousey); + final ItemsGridSlot hoverSlot = this.grid.getSlotMouseOver(mousex, mousey); if (hoverSlot != null) { if (button == 2) { - - if (hoverSlot.item != null) { - ItemPanels.itemPanel.draggedStack = getDraggedStackWithQuantity(hoverSlot.slotIndex); - } - + ItemPanels.itemPanel.draggedStack = getDraggedStackWithQuantity(hoverSlot.getItemStack()); } else { - mouseDownSlot = hoverSlot.slotIndex; + mouseDownItemIndex = hoverSlot.itemIndex; } return true; @@ -144,46 +180,38 @@ public class ItemHistoryPanel extends Widget { return false; } - @Override - public void mouseUp(int mousex, int mousey, int button) { - ItemPanelSlot hoverSlot = getSlotMouseOver(mousex, mousey); - - if (hoverSlot != null && hoverSlot.slotIndex == mouseDownSlot && ItemPanels.itemPanel.draggedStack == null) { - ItemStack item = hoverSlot.item.copy(); - - if (NEIController.manager.window instanceof GuiRecipe || NEIClientUtils.shiftKey() - || !NEIClientConfig.canCheatItem(item)) { - - if (button == 0) { - GuiCraftingRecipe.openRecipeGui("item", item); - } else if (button == 1) { - GuiUsageRecipe.openRecipeGui("item", item); - } - - mouseDownSlot = -1; - return; - } - - NEIClientUtils.cheatItem(getDraggedStackWithQuantity(hoverSlot.slotIndex), button, -1); - } - - mouseDownSlot = -1; + protected ItemStack getDraggedStackWithQuantity(ItemStack itemStack) { + return ItemQuantityField.prepareStackWithQuantity(itemStack, 0); } - protected ItemStack getDraggedStackWithQuantity(int mouseDownSlot) { - ItemStack stack = grid.getItem(mouseDownSlot); + public ItemStack getStackMouseOverWithQuantity(int mousex, int mousey) { + final ItemStack hoverSlot = getStackMouseOver(mousex, mousey); + return hoverSlot != null ? getDraggedStackWithQuantity(hoverSlot) : null; + } - if (stack != null) { - int amount = NEIClientConfig.showItemQuantityWidget() ? NEIClientConfig.getItemQuantity() : 0; + @Override + public void mouseUp(int mousex, int mousey, int button) { + final ItemsGridSlot hoverSlot = getSlotMouseOver(mousex, mousey); - if (amount == 0) { - amount = stack.getMaxStackSize(); + if (hoverSlot != null && hoverSlot.itemIndex == this.mouseDownItemIndex + && ItemPanels.itemPanel.draggedStack == null) { + + if (NEIController.manager.window instanceof GuiRecipe || NEIClientUtils.shiftKey() + || !NEIClientConfig.canCheatItem(hoverSlot.getItemStack())) { + + if (button == 0) { + GuiCraftingRecipe.openRecipeGui("item", hoverSlot.getItemStack().copy()); + } else if (button == 1) { + GuiUsageRecipe.openRecipeGui("item", hoverSlot.getItemStack().copy()); + } + + } else { + NEIClientUtils.cheatItem(getDraggedStackWithQuantity(hoverSlot.getItemStack()), button, -1); } - return NEIServerUtils.copyStack(stack, amount); } - return null; + this.mouseDownItemIndex = -1; } } diff --git a/src/main/java/codechicken/nei/ItemList.java b/src/main/java/codechicken/nei/ItemList.java index 42622ede..45bf8b6f 100644 --- a/src/main/java/codechicken/nei/ItemList.java +++ b/src/main/java/codechicken/nei/ItemList.java @@ -7,6 +7,7 @@ import java.util.HashMap; import java.util.HashSet; import java.util.LinkedList; import java.util.List; +import java.util.Map; import java.util.concurrent.ExecutionException; import java.util.concurrent.ForkJoinPool; import java.util.concurrent.ForkJoinWorkerThread; @@ -47,7 +48,7 @@ public class ItemList { private static final HashSet erroredItems = new HashSet<>(); private static final HashSet stackTraces = new HashSet<>(); - private static HashMap ordering = new HashMap<>(); + private static Map ordering = new HashMap<>(); /** * Unlike {@link LayoutManager#itemsLoaded}, this indicates whether item loading is actually finished or not. */ @@ -213,8 +214,7 @@ public class ItemList { IIcon icon = item.getIconIndex(itemstack); String name = getTooltip(itemstack); String s = name + "@" + (icon == null ? 0 : icon.hashCode()); - if (!damageIconSet.contains(s)) { - damageIconSet.add(s); + if (damageIconSet.add(s)) { permutations.add(itemstack); } } catch (TimeoutException t) { @@ -237,12 +237,11 @@ public class ItemList { } private void updateOrdering(List items) { + final Map newOrdering = new HashMap<>(); ItemSorter.sort(items); - HashMap newOrdering = new HashMap<>(); - if (!CollapsibleItems.isEmpty()) { - HashMap groups = new HashMap<>(); + final HashMap groups = new HashMap<>(); int orderIndex = 0; for (ItemStack stack : items) { @@ -309,7 +308,6 @@ public class ItemList { ListMultimap itemMap = ArrayListMultimap.create(); ItemStackSet unique = new ItemStackSet(); - timer.setLimit(NEIClientConfig.getItemLoadingTimeout()); StreamSupport.stream(((Iterable) Item.itemRegistry).spliterator(), true).forEach(item -> { if (item == null || item.delegate.name() == null || erroredItems.contains(item)) return; @@ -330,6 +328,7 @@ public class ItemList { } CollapsibleItems.putItem(stack); + TooltipFilter.getSearchTooltip(stack); } } @@ -358,13 +357,10 @@ public class ItemList { if (interrupted()) return; updateOrdering(ItemList.items); - new Thread( - () -> ItemList.items.parallelStream().forEach(TooltipFilter::getSearchTooltip), - "NEI Tooltip Filter Loader").start(); - loadFinished = true; SubsetWidget.updateHiddenItems(); + ItemPanels.bookmarkPanel.load(); updateFilter.restart(); } }; @@ -410,8 +406,7 @@ public class ItemList { if (interrupted()) return; - Comparator comparator = Comparator.comparingInt(ItemList.ordering::get); - filtered.sort(comparator::compare); + filtered.sort(Comparator.comparingInt(ItemList.ordering::get)); if (interrupted()) return; diff --git a/src/main/java/codechicken/nei/ItemPanel.java b/src/main/java/codechicken/nei/ItemPanel.java index 8a70292f..f3f6dce4 100644 --- a/src/main/java/codechicken/nei/ItemPanel.java +++ b/src/main/java/codechicken/nei/ItemPanel.java @@ -1,27 +1,17 @@ package codechicken.nei; -import java.awt.Color; import java.util.ArrayList; -import java.util.HashMap; -import java.util.HashSet; import java.util.List; -import java.util.Set; - -import javax.annotation.Nullable; +import java.util.Map; import net.minecraft.client.gui.inventory.GuiContainer; -import net.minecraft.client.renderer.OpenGlHelper; -import net.minecraft.client.renderer.Tessellator; import net.minecraft.item.ItemStack; -import net.minecraft.util.EnumChatFormatting; - -import org.lwjgl.opengl.GL11; import codechicken.lib.gui.GuiDraw; -import codechicken.lib.vec.Rectangle4i; -import codechicken.nei.guihook.GuiContainerManager; +import codechicken.nei.ItemsPanelGrid.ItemsPanelGridSlot; +import codechicken.nei.util.NEIMouseUtils; -public class ItemPanel extends PanelWidget { +public class ItemPanel extends PanelWidget { /** * Backwards compat :-/ @@ -41,285 +31,49 @@ public class ItemPanel extends PanelWidget { return super.getStackMouseOver(mousex, mousey); } - public ItemHistoryPanel historyPanel; + public ItemHistoryPanel historyPanel = new ItemHistoryPanel(); public Button toggleGroups; - public static class ItemPanelSlot { + @Deprecated + public static class ItemPanelSlot extends ItemsPanelGridSlot { - public ItemStack item; - public int slotIndex; - - public ItemPanelSlot(int idx, ItemStack stack) { - slotIndex = idx; - item = stack; + @Deprecated + public ItemPanelSlot(int idx, ItemStack item) { + this(idx, 0, item); } @Deprecated public ItemPanelSlot(int idx) { - this(idx, ItemPanels.itemPanel.getGrid().getItem(idx)); - } - } - - protected static class MaskMetadata { - - public int groupIndex = -1; - public String displayName = ""; - public boolean extended = false; - - public Color bgColor = null; - public Color color = null; - - public boolean left = false; - public boolean top = false; - public boolean right = false; - public boolean bottom = false; - - } - - protected static class ItemPanelGrid extends ItemsGrid { - - public ArrayList newItems; - public ArrayList rawItems; - - protected final HashMap> groupItems = new HashMap<>(); - protected final HashMap maskMetadata = new HashMap<>(); - protected boolean forceExpand = false; - - public void setItems(ArrayList items) { - newItems = items; + this(idx, 0, ItemPanels.itemPanel.getGrid().getItem(idx)); } - public void refresh(GuiContainer gui) { + private ItemPanelSlot(int slotIndex, int itemIndex, ItemStack item) { + super(slotIndex, itemIndex, item); + } - if (this.newItems != null) { - this.groupItems.clear(); - this.forceExpand = false; + protected static ItemPanelSlot of(ItemsPanelGridSlot slot) { - if (!CollapsibleItems.isEmpty() && !this.newItems.isEmpty()) { - final Set groups = new HashSet<>(); - boolean outsideGroup = false; - - this.realItems = new ArrayList<>(); - this.rawItems = new ArrayList<>(this.newItems); - - for (ItemStack stack : this.newItems) { - final int groupIndex = CollapsibleItems.getGroupIndex(stack); - - if (groupIndex == -1) { - this.realItems.add(stack); - outsideGroup = true; - } else { - - if (!groups.contains(groupIndex) || CollapsibleItems.isExpanded(groupIndex)) { - this.realItems.add(stack); - groups.add(groupIndex); - } - - if (!this.groupItems.containsKey(groupIndex)) { - this.groupItems.put(groupIndex, new ArrayList<>()); - } - - this.groupItems.get(groupIndex).add(stack); - } - } - - // automatically opens if there are elements from only one group - if (!outsideGroup && groups.size() == 1) { - this.realItems = new ArrayList<>(this.newItems); - this.forceExpand = true; - } - } else { - this.realItems = new ArrayList<>(this.newItems); - } - - this.newItems = null; - onItemsChanged(); + if (slot != null) { + ItemPanelSlot customSlot = new ItemPanelSlot(slot.slotIndex, slot.itemIndex, slot.item); + customSlot.groupIndex = slot.groupIndex; + customSlot.displayName = slot.displayName; + customSlot.bgItemStack = slot.bgItemStack; + customSlot.groupSize = slot.groupSize; + customSlot.extended = slot.extended; + customSlot.bgColor = slot.bgColor; + return customSlot; } - super.refresh(gui); - } - - @Override - protected List getMask() { - final boolean updateBorders = this.gridMask == null; - final List mask = super.getMask(); - - if (updateBorders) { - calculateGroupBorders(mask); - } - - return mask; - } - - protected void calculateGroupBorders(List mask) { - this.maskMetadata.clear(); - - if (mask.isEmpty() || this.groupItems.isEmpty()) { - return; - } - - final HashMap maskGroup = new HashMap<>(); - final Color collapsedColor = new Color( - NEIClientConfig.getSetting("inventory.collapsibleItems.collapsedColor").getHexValue(), - true); - final Color expandedColor = new Color( - NEIClientConfig.getSetting("inventory.collapsibleItems.expandedColor").getHexValue(), - true); - - for (int slotIndex = 0; slotIndex < mask.size(); slotIndex++) { - if (mask.get(slotIndex) == null) { - continue; - } - - int idx = mask.get(slotIndex); - maskGroup.put(idx, CollapsibleItems.getGroupIndex(getItem(idx))); - } - - for (int slotIndex = 0; slotIndex < mask.size(); slotIndex++) { - if (mask.get(slotIndex) == null) { - continue; - } - - int idx = mask.get(slotIndex); - int groupIndex = maskGroup.get(idx); - - if (groupIndex == -1 || this.groupItems.get(groupIndex).size() == 1) { - continue; - } - - int column = slotIndex % this.columns; - int row = slotIndex / this.columns; - int prevSlotIndex = (row - 1) * this.columns + column; - int nextSlotIndex = (row + 1) * this.columns + column; - MaskMetadata metadata = new MaskMetadata(); - - metadata.groupIndex = groupIndex; - metadata.displayName = CollapsibleItems.getDisplayName(groupIndex); - metadata.extended = this.forceExpand || CollapsibleItems.isExpanded(groupIndex); - metadata.bgColor = metadata.extended ? expandedColor : collapsedColor; - metadata.color = darkerColor(metadata.bgColor); - metadata.left = column == 0 || idx == 0 - || mask.get(slotIndex - 1) == null - || maskGroup.getOrDefault(idx - 1, -1) != groupIndex; - metadata.right = column == this.columns - 1 || maskGroup.getOrDefault(idx + 1, -1) != groupIndex; - - if (prevSlotIndex >= 0) { - Integer previdx = mask.get(prevSlotIndex); - metadata.top = previdx == null || maskGroup.getOrDefault(previdx, -1) != groupIndex; - } else { - metadata.top = true; - } - - if (nextSlotIndex < mask.size()) { - Integer nextidx = mask.get(nextSlotIndex); - metadata.bottom = nextidx == null || maskGroup.getOrDefault(nextidx, -1) != groupIndex; - } else { - metadata.bottom = true; - } - - this.maskMetadata.put(idx, metadata); - } - } - - @Override - protected void beforeDrawItems(int mousex, int mousey, @Nullable ItemPanelSlot focused) { - final List mask = getMask(); - - super.beforeDrawItems(mousex, mousey, focused); - - for (int i = 0; i < mask.size(); i++) { - if (mask.get(i) != null) { - drawBorder(mask.get(i), getSlotRect(i)); - } - } - } - - protected void drawBorder(int slotIdx, Rectangle4i rect) { - final MaskMetadata metadata = this.maskMetadata.get(slotIdx); - - if (metadata != null) { - final float LINE_WIDTH = 0.75f; - - drawRect(rect.x, rect.y, rect.w, rect.h, metadata.bgColor); - - if (metadata.left) { - float leftBottom = metadata.bottom ? -LINE_WIDTH : 0; - drawRect(rect.x, rect.y, LINE_WIDTH, rect.h + leftBottom, metadata.color); - } - - if (metadata.right) { - float rightTop = metadata.top ? LINE_WIDTH : 0; - drawRect(rect.x + rect.w, rect.y - rightTop, LINE_WIDTH, rect.h + rightTop, metadata.color); - } - - if (metadata.top) { - drawRect(rect.x, rect.y - LINE_WIDTH, rect.w, LINE_WIDTH, metadata.color); - } - - if (metadata.bottom) { - drawRect(rect.x, rect.y + rect.h - LINE_WIDTH, rect.w, LINE_WIDTH, metadata.color); - } - } - } - - private static Color darkerColor(Color color) { - return new Color( - color.getRed(), - color.getGreen(), - color.getBlue(), - Math.min((int) (color.getAlpha() + (255f / 5) * 2), 255)); - } - - private static void drawRect(double left, double top, double width, double height, Color color) { - Tessellator tessellator = Tessellator.instance; - - GL11.glEnable(GL11.GL_BLEND); - GL11.glDisable(GL11.GL_TEXTURE_2D); - OpenGlHelper.glBlendFunc(770, 771, 1, 0); - GL11.glColor4f( - color.getRed() / 255f, - color.getGreen() / 255f, - color.getBlue() / 255f, - color.getAlpha() / 255f); - tessellator.startDrawingQuads(); - tessellator.addVertex(left, top + height, 0.0D); - tessellator.addVertex(left + width, top + height, 0.0D); - tessellator.addVertex(left + width, top, 0.0D); - tessellator.addVertex(left, top, 0.0D); - tessellator.draw(); - GL11.glEnable(GL11.GL_TEXTURE_2D); - GL11.glDisable(GL11.GL_BLEND); - } - - @Override - protected void drawItem(Rectangle4i rect, int slotIdx) { - final MaskMetadata metadata = this.maskMetadata.get(slotIdx); - - if (metadata != null && !metadata.extended) { - final List groupItems = this.groupItems.get(metadata.groupIndex); - - GuiContainerManager.drawItems.zLevel -= 10F; - GuiContainerManager.drawItem(rect.x + 1, rect.y - 1, groupItems.get(groupItems.size() - 1), true, ""); - GuiContainerManager.drawItems.zLevel += 10F; - - GuiContainerManager.drawItem(rect.x - 1, rect.y + 1, getItem(slotIdx), true, ""); - } else { - super.drawItem(rect, slotIdx); - } - } - - @Override - public String getMessageOnEmpty() { - return ItemList.loadFinished ? null : NEIClientUtils.translate("itempanel.loading"); + return null; } } public ItemPanel() { - grid = new ItemPanelGrid(); + this.grid = new ItemsPanelGrid(); } public static void updateItemList(ArrayList newItems) { - ((ItemPanelGrid) ItemPanels.itemPanel.getGrid()).setItems(newItems); + ItemPanels.itemPanel.getGrid().setItems(newItems); ItemPanels.itemPanel.realItems = newItems; } @@ -342,12 +96,12 @@ public class ItemPanel extends PanelWidget { } }; - historyPanel = new ItemHistoryPanel(); } - @Deprecated - public void scroll(int i) { - grid.shiftPage(i); + @Override + public ItemPanelSlot getSlotMouseOver(int mousex, int mousey) { + // use ItemPanelSlot for Backwards compat + return ItemPanelSlot.of(this.grid.getSlotMouseOver(mousex, mousey)); } public String getLabelText() { @@ -395,7 +149,7 @@ public class ItemPanel extends PanelWidget { if (NEIClientConfig.showHistoryPanelWidget()) { historyPanel.x = x; historyPanel.w = w; - historyPanel.h = ItemsGrid.SLOT_SIZE * NEIClientConfig.getIntSetting("inventory.history.useRows"); + historyPanel.h = 8 + ItemsGrid.SLOT_SIZE * NEIClientConfig.getIntSetting("inventory.history.useRows"); if (NEIClientConfig.showItemQuantityWidget() || !NEIClientConfig.isSearchWidgetCentered()) { historyPanel.y = LayoutManager.quantity.y - historyPanel.h - PanelWidget.PADDING; @@ -430,51 +184,23 @@ public class ItemPanel extends PanelWidget { historyPanel.resize(gui); } - protected ItemStack getDraggedStackWithQuantity(int mouseDownSlot) { - ItemStack stack = grid.getItem(mouseDownSlot); - - if (stack != null) { - int amount = NEIClientConfig.showItemQuantityWidget() ? NEIClientConfig.getItemQuantity() : 0; - - if (amount == 0) { - amount = stack.getMaxStackSize(); - } - - return NEIServerUtils.copyStack(stack, amount); - } - - return null; + protected ItemStack getDraggedStackWithQuantity(ItemStack itemStack) { + return ItemQuantityField.prepareStackWithQuantity(itemStack, 0); } @Override public List handleItemTooltip(GuiContainer gui, ItemStack itemstack, int mousex, int mousey, List currenttip) { - final ItemPanelGrid panelGrid = ((ItemPanelGrid) this.grid); - if (!panelGrid.forceExpand && !NEIClientConfig.isHidden() && !currenttip.isEmpty()) { - final ItemPanelSlot hoverSlot = getSlotMouseOver(mousex, mousey); + if (!this.grid.forceExpand && !currenttip.isEmpty()) { + final ItemsPanelGridSlot hoverSlot = this.grid.getSlotMouseOver(mousex, mousey); - if (hoverSlot != null) { - final MaskMetadata metadata = panelGrid.maskMetadata.get(hoverSlot.slotIndex); - - if (metadata != null) { - final List items = panelGrid.groupItems.get(metadata.groupIndex); - - if (NEIClientConfig.getBooleanSetting("inventory.collapsibleItems.customName") && !metadata.extended - && !"".equals(metadata.displayName)) { - currenttip.clear(); - currenttip.add(metadata.displayName + GuiDraw.TOOLTIP_LINESPACE); - } - - if (items != null && items.size() > 1) { - String message = metadata.extended ? "itempanel.collapsed.hint.collapse" - : "itempanel.collapsed.hint.expand"; - currenttip.add( - 1, - EnumChatFormatting.GRAY + NEIClientUtils.translate(message, items.size()) - + EnumChatFormatting.RESET); - } - } + if (hoverSlot != null && hoverSlot.groupIndex >= 0 + && NEIClientConfig.getBooleanSetting("inventory.collapsibleItems.customName") + && !hoverSlot.extended + && !"".equals(hoverSlot.displayName)) { + currenttip.clear(); + currenttip.add(hoverSlot.displayName + GuiDraw.TOOLTIP_LINESPACE); } } @@ -483,23 +209,34 @@ public class ItemPanel extends PanelWidget { @Override public boolean handleClick(int mousex, int mousey, int button) { - final ItemPanelGrid panelGrid = ((ItemPanelGrid) this.grid); - if (NEIClientUtils.altKey() && button == 0 && !panelGrid.forceExpand) { - final ItemPanelSlot hoverSlot = grid.getSlotMouseOver(mousex, mousey); + if (button == 0 && !this.grid.forceExpand && NEIClientUtils.altKey()) { + final ItemsPanelGridSlot hoverSlot = this.grid.getSlotMouseOver(mousex, mousey); - if (hoverSlot != null) { - final MaskMetadata metadata = panelGrid.maskMetadata.get(hoverSlot.slotIndex); - - if (metadata != null) { - CollapsibleItems.setExpanded(metadata.groupIndex, !metadata.extended); - panelGrid.setItems(panelGrid.rawItems); - return true; - } + if (hoverSlot != null && hoverSlot.groupIndex >= 0) { + CollapsibleItems.setExpanded(hoverSlot.groupIndex, !hoverSlot.extended); + this.grid.setItems(this.grid.rawItems); + NEIClientUtils.playClickSound(); + return true; } } return super.handleClick(mousex, mousey, button); } + @Override + public Map handleHotkeys(GuiContainer gui, int mousex, int mousey, Map hotkeys) { + final ItemsPanelGridSlot hoverSlot = this.grid.getSlotMouseOver(mousex, mousey); + + if (!this.grid.forceExpand && hoverSlot != null && hoverSlot.groupIndex >= 0 && hoverSlot.groupSize > 1) { + final String message = hoverSlot.extended ? "itempanel.collapsed.hint.collapse" + : "itempanel.collapsed.hint.expand"; + hotkeys.put( + NEIClientUtils.getKeyName(NEIClientUtils.ALT_HASH, NEIMouseUtils.MOUSE_BTN_LMB), + NEIClientUtils.translate(message, hoverSlot.groupSize)); + } + + return hotkeys; + } + } diff --git a/src/main/java/codechicken/nei/ItemQuantityField.java b/src/main/java/codechicken/nei/ItemQuantityField.java index b1bd01f7..23c91927 100644 --- a/src/main/java/codechicken/nei/ItemQuantityField.java +++ b/src/main/java/codechicken/nei/ItemQuantityField.java @@ -2,6 +2,10 @@ package codechicken.nei; import static codechicken.nei.NEIClientUtils.translate; +import net.minecraft.item.ItemStack; + +import codechicken.nei.recipe.StackInfo; + public class ItemQuantityField extends TextField { public ItemQuantityField(String ident) { @@ -22,6 +26,22 @@ public class ItemQuantityField extends TextField { } } + public static ItemStack prepareStackWithQuantity(ItemStack stack, long amount) { + if (stack == null) { + return null; + } + + if (amount == 0) { + amount = NEIClientConfig.showItemQuantityWidget() ? NEIClientConfig.getItemQuantity() : 0; + + if (amount == 0) { + amount = StackInfo.itemStackToNBT(stack).hasKey("gtFluidName") ? 144 : stack.getMaxStackSize(); + } + } + + return StackInfo.withAmount(stack, amount); + } + @Override public void gainFocus() { if (text().equals("0")) { diff --git a/src/main/java/codechicken/nei/ItemStackAmount.java b/src/main/java/codechicken/nei/ItemStackAmount.java index 77765f85..0fff933c 100644 --- a/src/main/java/codechicken/nei/ItemStackAmount.java +++ b/src/main/java/codechicken/nei/ItemStackAmount.java @@ -22,6 +22,12 @@ public class ItemStackAmount { } } + public void addAll(Iterable items) { + for (ItemStack stack : items) { + add(stack); + } + } + public void add(ItemStack item) { add(item, (long) StackInfo.getAmount(item)); } @@ -30,7 +36,9 @@ public class ItemStackAmount { if (stack == null || stack.getItem() == null) return; final NBTTagCompound key = StackInfo.itemStackToNBT(stack, false); - this.itemMap.put(key, value + this.itemMap.getOrDefault(key, 0L)); + if (key != null) { + this.itemMap.put(key, value + this.itemMap.getOrDefault(key, 0L)); + } } public Long get(ItemStack stack) { @@ -44,7 +52,9 @@ public class ItemStackAmount { if (stack == null || stack.getItem() == null) return; final NBTTagCompound key = StackInfo.itemStackToNBT(stack, false); - this.itemMap.put(key, value); + if (key != null) { + this.itemMap.put(key, value); + } } public long getOrDefault(ItemStack stack, long defaultAmount) { @@ -53,6 +63,10 @@ public class ItemStackAmount { return e == null ? defaultAmount : e; } + public boolean contains(ItemStack stack) { + return get(stack) != null; + } + public void clear() { this.itemMap.clear(); } diff --git a/src/main/java/codechicken/nei/ItemsGrid.java b/src/main/java/codechicken/nei/ItemsGrid.java index 636325c8..9b440af6 100644 --- a/src/main/java/codechicken/nei/ItemsGrid.java +++ b/src/main/java/codechicken/nei/ItemsGrid.java @@ -1,12 +1,10 @@ package codechicken.nei; -import static codechicken.lib.gui.GuiDraw.drawRect; - +import java.awt.Color; import java.util.ArrayList; import java.util.Arrays; import java.util.List; - -import javax.annotation.Nullable; +import java.util.Objects; import net.minecraft.client.Minecraft; import net.minecraft.client.gui.ScaledResolution; @@ -20,14 +18,13 @@ import org.lwjgl.opengl.GL11; import org.lwjgl.opengl.GL30; import codechicken.lib.vec.Rectangle4i; -import codechicken.nei.ItemPanel.ItemPanelSlot; import codechicken.nei.api.GuiInfo; import codechicken.nei.guihook.GuiContainerManager; import codechicken.nei.recipe.StackInfo; -public class ItemsGrid { +public abstract class ItemsGrid { - private static class ScreenCapture { + protected static class ScreenCapture { private long nextCacheRefresh = 0; private Framebuffer framebuffer; @@ -140,7 +137,126 @@ public class ItemsGrid { } - public static final int SLOT_SIZE = 18; + public static class ItemsGridSlot { + + protected static final float DEFAULT_SLOT_SIZE = 18f; + protected static final float LINE_WIDTH = 0.75f; + + public final ItemStack item; + public final int slotIndex; + public final int itemIndex; + + public Color borderColor = Color.BLACK; + + public boolean borderLeft = false; + public boolean borderTop = false; + public boolean borderRight = false; + public boolean borderBottom = false; + + public ItemsGridSlot(int slotIndex, int itemIndex, ItemStack itemStack) { + this.slotIndex = slotIndex; + this.itemIndex = itemIndex; + this.item = itemStack; + } + + public ItemStack getItemStack() { + return this.item; + } + + public void beforeDraw(Rectangle4i rect, M mouseContext) { + if (mouseContext != null && mouseContext.slotIndex == this.slotIndex) { + NEIClientUtils.drawRect(rect.x, rect.y, rect.w, rect.h, HIGHLIGHT_COLOR); + } + } + + public void afterDraw(Rectangle4i rect, M mouseContext) {} + + public void drawItem(Rectangle4i rect) { + drawItem(getItemStack(), rect); + } + + protected void drawItem(ItemStack stack, Rectangle4i rect) { + + if (rect.w != DEFAULT_SLOT_SIZE) { + final float panelFactor = (rect.w - 2) / (DEFAULT_SLOT_SIZE - 2); + GL11.glTranslatef(rect.x + 1, rect.y + 1, 0); + GL11.glScaled(panelFactor, panelFactor, 1); + + GuiContainerManager.drawItem(0, 0, stack, true, ""); + + GL11.glScaled(1f / panelFactor, 1f / panelFactor, 1); + GL11.glTranslatef(-1 * (rect.x + 1), -1 * (rect.y + 1), 0); + } else { + GuiContainerManager.drawItem(rect.x + 1, rect.y + 1, stack, true, ""); + } + + } + + public void drawBorder(Rectangle4i rect) { + float halfLineWidth = LINE_WIDTH / 2f; + + if (this.borderLeft) { + NEIClientUtils + .drawRect(rect.x - halfLineWidth, rect.y + halfLineWidth, LINE_WIDTH, rect.h, this.borderColor); + } + + if (this.borderRight) { + NEIClientUtils.drawRect( + rect.x + rect.w - halfLineWidth, + rect.y - halfLineWidth, + LINE_WIDTH, + rect.h, + this.borderColor); + } + + if (this.borderTop) { + NEIClientUtils + .drawRect(rect.x - halfLineWidth, rect.y - halfLineWidth, rect.w, LINE_WIDTH, this.borderColor); + } + + if (this.borderBottom) { + NEIClientUtils.drawRect( + rect.x + halfLineWidth, + rect.y + rect.h - halfLineWidth, + rect.w, + LINE_WIDTH, + this.borderColor); + } + } + + } + + public static class MouseContext { + + public final int slotIndex; + public final int columnIndex; + public final int rowIndex; + + public MouseContext(int slotIndex, int rowIndex, int columnIndex) { + this.slotIndex = slotIndex; + this.rowIndex = rowIndex; + this.columnIndex = columnIndex; + } + + @Override + public int hashCode() { + return Objects.hash(this.rowIndex, this.slotIndex, this.columnIndex); + } + + public boolean equals(Object obj) { + + if (obj instanceof MouseContext context) { + return this.columnIndex == context.columnIndex && this.rowIndex == context.rowIndex + && this.slotIndex == context.slotIndex; + } + + return false; + } + + } + + public static int SLOT_SIZE = 18; + public static final Color HIGHLIGHT_COLOR = new Color(0xee555555, true); protected int width; protected int height; @@ -157,7 +273,6 @@ public class ItemsGrid { protected int rows; protected int columns; - protected List gridMask = null; protected boolean[] invalidSlotMap; @@ -169,8 +284,8 @@ public class ItemsGrid { return realItems; } - public ItemStack getItem(int idx) { - return realItems.get(idx); + public ItemStack getItem(int itemIndex) { + return realItems.get(itemIndex); } public int size() { @@ -222,7 +337,11 @@ public class ItemsGrid { } public int getLastRowIndex() { - return this.columns > 0 ? (int) Math.ceil((float) this.getMask().size() / this.columns) - 1 : 0; + final List mask = getMask(); + + return this.columns > 0 && !mask.isEmpty() + ? (int) Math.ceil((float) (mask.get(mask.size() - 1).slotIndex + 1) / this.columns) - 1 + : 0; } public void setGridSize(int mleft, int mtop, int w, int h) { @@ -270,10 +389,9 @@ public class ItemsGrid { if (this.screenCapture != null) { this.screenCapture.refreshBuffer(); } - this.gridMask = null; } - protected void onItemsChanged() { + public void onItemsChanged() { onGridChanged(); } @@ -323,20 +441,37 @@ public class ItemsGrid { } } - public Rectangle4i getItemRect(int idx) { - final List mask = getMask(); - - for (int i = 0; i < mask.size(); i++) { - if (mask.get(i) != null && idx == mask.get(i)) { - return getSlotRect(i); - } + public T getSlotMouseOver(int mousex, int mousey) { + if (!contains(mousex, mousey)) { + return null; } - return null; + final int overRow = getRowIndex(mousey); + final int overColumn = getColumnIndex(mousex); + final int slt = columns * overRow + overColumn; + + if (overRow >= rows || overColumn >= columns) { + return null; + } + + return getSlotBySlotIndex(slt); } - public Rectangle4i getSlotRect(int i) { - return getSlotRect(i / columns, i % columns); + public T getSlotBySlotIndex(int slotIndex) { + return getMask().stream().filter(item -> item.slotIndex == slotIndex).findAny().orElse(null); + } + + public T getSlotByItemIndex(int itemIndex) { + return getMask().stream().filter(item -> item.itemIndex == itemIndex).findAny().orElse(null); + } + + public Rectangle4i getItemRect(int itemIndex) { + final T item = getSlotByItemIndex(itemIndex); + return item != null ? getSlotRect(item.slotIndex) : null; + } + + public Rectangle4i getSlotRect(int slotIndex) { + return getSlotRect(slotIndex / columns, slotIndex % columns); } public Rectangle4i getSlotRect(int row, int column) { @@ -347,67 +482,45 @@ public class ItemsGrid { SLOT_SIZE); } + public int getRowIndex(int mousey) { + return (mousey - this.marginTop) / SLOT_SIZE; + } + + public int getColumnIndex(int mousex) { + return (mousex - this.marginLeft - this.paddingLeft) / SLOT_SIZE; + } + public boolean isInvalidSlot(int index) { return invalidSlotMap[index]; } - protected List getMask() { + public abstract List getMask(); - if (this.gridMask == null) { - this.gridMask = new ArrayList<>(); - int idx = page * perPage; - - for (int i = 0; i < rows * columns && idx < size(); i++) { - this.gridMask.add(isInvalidSlot(i) ? null : idx++); - } + protected abstract M getMouseContext(int mousex, int mousey); + protected void beforeDrawItems(int mousex, int mousey, M mouseContext) { + for (T item : getMask()) { + item.beforeDraw(getSlotRect(item.slotIndex), mouseContext); } - - return this.gridMask; - } - - protected void beforeDrawItems(int mousex, int mousey, @Nullable ItemPanelSlot focused) { - - final List mask = getMask(); - - for (int i = 0; i < mask.size(); i++) { - if (mask.get(i) != null) { - beforeDrawSlot(focused, mask.get(i), getSlotRect(i)); - } + for (T item : getMask()) { + item.drawBorder(getSlotRect(item.slotIndex)); } } - protected void afterDrawItems(int mousex, int mousey, @Nullable ItemPanelSlot focused) { - final List mask = getMask(); - - for (int i = 0; i < mask.size(); i++) { - if (mask.get(i) != null) { - afterDrawSlot(focused, mask.get(i), getSlotRect(i)); - } + protected void afterDrawItems(int mousex, int mousey, M mouseContext) { + for (T item : getMask()) { + item.afterDraw(getSlotRect(item.slotIndex), mouseContext); } } public void update() {} - protected void beforeDrawSlot(@Nullable ItemPanelSlot focused, int slotIdx, Rectangle4i rect) { - if (focused != null && focused.slotIndex == slotIdx) { - drawRect(rect.x, rect.y, rect.w, rect.h, 0xee555555); // highlight - } - } + protected void drawItems() { - protected void afterDrawSlot(@Nullable ItemPanelSlot focused, int slotIdx, Rectangle4i rect) {} - - private void drawItems() { - GuiContainerManager.enableMatrixStackLogging(); - - final List mask = getMask(); - for (int i = 0; i < mask.size(); i++) { - if (mask.get(i) != null) { - drawItem(getSlotRect(i), mask.get(i)); - } + for (T item : getMask()) { + item.drawItem(getSlotRect(item.slotIndex)); } - GuiContainerManager.disableMatrixStackLogging(); } protected int getGridRenderingCacheMode() { @@ -419,10 +532,10 @@ public class ItemsGrid { return; } - final ItemPanelSlot focused = getSlotMouseOver(mousex, mousey); final int gridRenderingCacheMode = getGridRenderingCacheMode(); + final M mouseContext = getMouseContext(mousex, mousey); - beforeDrawItems(mousex, mousey, focused); + beforeDrawItems(mousex, mousey, mouseContext); if (gridRenderingCacheMode > 0) { @@ -431,15 +544,21 @@ public class ItemsGrid { } if (this.screenCapture.needRefresh(gridRenderingCacheMode)) { - this.screenCapture.captureScreen(this::drawItems, gridRenderingCacheMode); + this.screenCapture.captureScreen(() -> { + GuiContainerManager.enableMatrixStackLogging(); + drawItems(); + GuiContainerManager.disableMatrixStackLogging(); + }, gridRenderingCacheMode); } this.screenCapture.renderCapturedScreen(); } else { + GuiContainerManager.enableMatrixStackLogging(); drawItems(); + GuiContainerManager.disableMatrixStackLogging(); } - afterDrawItems(mousex, mousey, focused); + afterDrawItems(mousex, mousey, mouseContext); } public void setVisible() { @@ -448,34 +567,6 @@ public class ItemsGrid { } } - protected void drawItem(Rectangle4i rect, int idx) { - GuiContainerManager.drawItem(rect.x + 1, rect.y + 1, getItem(idx)); - } - - public ItemPanelSlot getSlotMouseOver(int mousex, int mousey) { - if (!contains(mousex, mousey)) { - return null; - } - - final int overRow = (mousey - marginTop) / SLOT_SIZE; - final int overColumn = (mousex - marginLeft - paddingLeft) / SLOT_SIZE; - final int slt = columns * overRow + overColumn; - - if (overRow >= rows || overColumn >= columns) { - return null; - } - - final List mask = getMask(); - - if (mask.size() > slt && mask.get(slt) != null) { - final int idx = mask.get(slt); - - return new ItemPanelSlot(idx, getItem(idx)); - } - - return null; - } - public boolean contains(int px, int py) { final Rectangle4i rect = new Rectangle4i( marginLeft + paddingLeft, @@ -487,10 +578,7 @@ public class ItemsGrid { return false; } - final int r = (py - marginTop) / SLOT_SIZE; - final int c = (px - marginLeft - paddingLeft) / SLOT_SIZE; - - return !isInvalidSlot(columns * r + c); + return !isInvalidSlot(columns * getRowIndex(py) + getColumnIndex(px)); } public String getMessageOnEmpty() { diff --git a/src/main/java/codechicken/nei/ItemsPanelGrid.java b/src/main/java/codechicken/nei/ItemsPanelGrid.java new file mode 100644 index 00000000..90a79f4b --- /dev/null +++ b/src/main/java/codechicken/nei/ItemsPanelGrid.java @@ -0,0 +1,239 @@ +package codechicken.nei; + +import java.awt.Color; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.HashSet; +import java.util.List; +import java.util.Map; +import java.util.Set; + +import net.minecraft.client.gui.inventory.GuiContainer; +import net.minecraft.item.ItemStack; + +import codechicken.lib.vec.Rectangle4i; +import codechicken.nei.ItemsGrid.ItemsGridSlot; +import codechicken.nei.guihook.GuiContainerManager; + +public class ItemsPanelGrid extends ItemsGrid { + + public static class ItemsPanelGridSlot extends ItemsGridSlot { + + protected static final float LINE_WIDTH = 0.75f; + + public int groupIndex = -1; + public String displayName = ""; + public ItemStack bgItemStack; + public int groupSize = 1; + public boolean extended = false; + + public Color bgColor = null; + + public ItemsPanelGridSlot(int slotIndex, int itemIndex, ItemStack itemStack) { + super(slotIndex, itemIndex, itemStack); + } + + public ItemStack getBGItem() { + return this.bgItemStack; + } + + @Override + public void beforeDraw(Rectangle4i rect, MouseContext mouseContext) { + super.beforeDraw(rect, mouseContext); + + if (this.bgColor != null) { + NEIClientUtils.drawRect(rect.x, rect.y, rect.w, rect.h, this.bgColor); + } + + } + + @Override + public void drawItem(Rectangle4i rect) { + + if (!this.extended && getBGItem() != null) { + GuiContainerManager.drawItems.zLevel -= 10F; + super.drawItem(getBGItem(), rect.offset(1, -1)); + GuiContainerManager.drawItems.zLevel += 10F; + super.drawItem(getItemStack(), rect.offset(-2, 2)); + } else { + super.drawItem(rect); + } + } + + } + + public ArrayList newItems; + public ArrayList rawItems; + protected List gridMask; + + protected final Map itemToGroup = new HashMap<>(); + protected final Map> groupItems = new HashMap<>(); + protected boolean forceExpand = false; + + public void setItems(ArrayList items) { + newItems = items; + } + + public static void updateScale() { + SLOT_SIZE = 2 + (int) Math.ceil(8f * NEIClientConfig.getIntSetting("inventory.itempanelScale") / 100) * 2; + } + + @Override + public void refresh(GuiContainer gui) { + + if (this.newItems != null) { + this.groupItems.clear(); + this.itemToGroup.clear(); + this.forceExpand = false; + + if (!CollapsibleItems.isEmpty() && !this.newItems.isEmpty()) { + final Set groups = new HashSet<>(); + boolean outsideGroup = false; + + this.realItems = new ArrayList<>(); + this.rawItems = new ArrayList<>(this.newItems); + + for (ItemStack stack : this.newItems) { + final int groupIndex = CollapsibleItems.getGroupIndex(stack); + + if (groupIndex == -1) { + this.realItems.add(stack); + outsideGroup = true; + } else { + + if (!groups.contains(groupIndex) || CollapsibleItems.isExpanded(groupIndex)) { + this.itemToGroup.put(this.realItems.size(), groupIndex); + this.realItems.add(stack); + groups.add(groupIndex); + } + + this.groupItems.computeIfAbsent(groupIndex, gi -> new ArrayList<>()).add(stack); + } + } + + // automatically opens if there are elements from only one group + if (!outsideGroup && groups.size() == 1) { + final int groupIndex = groups.iterator().next(); + this.realItems = new ArrayList<>(this.newItems); + this.itemToGroup.clear(); + this.forceExpand = true; + + for (int itemIndex = 0; itemIndex < this.realItems.size(); itemIndex++) { + this.itemToGroup.put(itemIndex, groupIndex); + } + } + } else { + this.realItems = new ArrayList<>(this.newItems); + } + + this.newItems = null; + onItemsChanged(); + } + + super.refresh(gui); + } + + @Override + protected void onGridChanged() { + this.gridMask = null; + super.onGridChanged(); + } + + @Override + public List getMask() { + + if (this.gridMask == null) { + this.gridMask = new ArrayList<>(); + int itemIndex = page * perPage; + + for (int slotIndex = 0; slotIndex < rows * columns && itemIndex < size(); slotIndex++) { + if (!isInvalidSlot(slotIndex)) { + this.gridMask.add(new ItemsPanelGridSlot(slotIndex, itemIndex, getItem(itemIndex))); + itemIndex++; + } + } + + calculateGroupBorders(this.gridMask); + } + + return this.gridMask; + } + + @Override + protected MouseContext getMouseContext(int mousex, int mousey) { + final ItemsPanelGridSlot hovered = getSlotMouseOver(mousex, mousey); + + if (hovered != null) { + return new MouseContext( + hovered.slotIndex, + hovered.slotIndex / this.columns, + hovered.slotIndex % this.columns); + } + + return null; + } + + protected void calculateGroupBorders(List gridMask) { + + if (gridMask.isEmpty() || this.groupItems.isEmpty()) { + return; + } + + final Color collapsedBGColor = new Color( + NEIClientConfig.getSetting("inventory.collapsibleItems.collapsedColor").getHexValue(), + true); + final Color expandedBGColor = new Color( + NEIClientConfig.getSetting("inventory.collapsibleItems.expandedColor").getHexValue(), + true); + final Color collapsedBorderColor = darkerColor(collapsedBGColor); + final Color expandedBorderColor = darkerColor(expandedBGColor); + final Map borderGrid = new HashMap<>(); + + for (ItemsPanelGridSlot item : gridMask) { + borderGrid.put(item.slotIndex, this.itemToGroup.getOrDefault(item.itemIndex, -1)); + } + + for (ItemsPanelGridSlot item : gridMask) { + final int groupIndex = borderGrid.get(item.slotIndex); + final List groupItems = this.groupItems.get(groupIndex); + + if (groupItems == null || groupItems.size() == 1) { + continue; + } + + int column = item.slotIndex % this.columns; + int row = item.slotIndex / this.columns; + int prevSlotIndex = (row - 1) * this.columns + column; + int nextSlotIndex = (row + 1) * this.columns + column; + + item.groupIndex = groupIndex; + item.groupSize = groupItems.size(); + item.bgItemStack = groupItems.get(groupItems.size() - 1); + item.displayName = CollapsibleItems.getDisplayName(groupIndex); + item.extended = this.forceExpand || CollapsibleItems.isExpanded(groupIndex); + item.bgColor = item.extended ? expandedBGColor : collapsedBGColor; + + item.borderColor = item.extended ? expandedBorderColor : collapsedBorderColor; + item.borderLeft = column == 0 || borderGrid.getOrDefault(item.slotIndex - 1, -1) != groupIndex; + item.borderRight = column + 1 == this.columns + || borderGrid.getOrDefault(item.slotIndex + 1, -1) != groupIndex; + item.borderTop = borderGrid.getOrDefault(prevSlotIndex, -1) != groupIndex; + item.borderBottom = borderGrid.getOrDefault(nextSlotIndex, -1) != groupIndex; + } + + } + + private static Color darkerColor(Color color) { + return new Color( + color.getRed(), + color.getGreen(), + color.getBlue(), + Math.min((int) (color.getAlpha() + (255f / 5) * 2), 255)); + } + + @Override + public String getMessageOnEmpty() { + return ItemList.loadFinished ? null : NEIClientUtils.translate("itempanel.loading"); + } + +} diff --git a/src/main/java/codechicken/nei/ItemsTooltipLineHandler.java b/src/main/java/codechicken/nei/ItemsTooltipLineHandler.java index 56246f10..119b16e2 100644 --- a/src/main/java/codechicken/nei/ItemsTooltipLineHandler.java +++ b/src/main/java/codechicken/nei/ItemsTooltipLineHandler.java @@ -3,15 +3,19 @@ package codechicken.nei; import static codechicken.lib.gui.GuiDraw.fontRenderer; import java.awt.Dimension; +import java.util.ArrayList; import java.util.List; +import java.util.Map; import net.minecraft.client.renderer.RenderHelper; import net.minecraft.item.ItemStack; +import net.minecraft.nbt.NBTTagCompound; import net.minecraft.util.EnumChatFormatting; import org.lwjgl.opengl.GL11; import org.lwjgl.opengl.GL12; +import codechicken.lib.gui.GuiDraw; import codechicken.lib.gui.GuiDraw.ITooltipLineHandler; import codechicken.nei.guihook.GuiContainerManager; import codechicken.nei.recipe.StackInfo; @@ -19,12 +23,14 @@ import codechicken.nei.util.ReadableNumberConverter; public class ItemsTooltipLineHandler implements ITooltipLineHandler { + protected static final int SLOT_SIZE = 18; protected static final int MAX_COLUMNS = 11; protected static final int MARGIN_TOP = 2; protected String label; protected EnumChatFormatting labelColor = EnumChatFormatting.GRAY; protected List items; + protected int activeStackIndex = -1; protected Dimension size = new Dimension(); protected boolean saveStackSize = false; protected int columns = 0; @@ -46,9 +52,8 @@ public class ItemsTooltipLineHandler implements ITooltipLineHandler { this.columns = Math.min(MAX_COLUMNS, this.length); this.rows = Math.min(maxRows, (int) Math.ceil((float) this.length / this.columns)); - this.size.width = Math - .max(this.columns * ItemsGrid.SLOT_SIZE, fontRenderer.getStringWidth(this.label) + 15); - this.size.height = this.rows * ItemsGrid.SLOT_SIZE + fontRenderer.FONT_HEIGHT + 2 + MARGIN_TOP; + this.size.width = Math.max(this.columns * SLOT_SIZE, fontRenderer.getStringWidth(this.label) + 15); + this.size.height = this.rows * SLOT_SIZE + fontRenderer.FONT_HEIGHT + 2 + MARGIN_TOP; this.count = Math.min( this.length, @@ -58,12 +63,22 @@ public class ItemsTooltipLineHandler implements ITooltipLineHandler { if (this.items.size() > this.count) { String text = "+" + (this.items.size() - this.count); - this.count -= (int) Math.ceil((float) (fontRenderer.getStringWidth(text) - 2) / ItemsGrid.SLOT_SIZE); + this.count -= (int) Math.ceil((float) (fontRenderer.getStringWidth(text) - 2) / SLOT_SIZE); } } } + public void setActiveStack(ItemStack activeStack) { + final ItemStack realStack = items.stream().filter(stack -> StackInfo.equalItemAndNBT(stack, activeStack, true)) + .findFirst().orElse(null); + this.activeStackIndex = items.indexOf(realStack); + } + + public ItemStack getActiveStack() { + return this.activeStackIndex == -1 ? null : this.items.get(this.activeStackIndex); + } + public boolean isEmpty() { return this.items.isEmpty(); } @@ -93,26 +108,38 @@ public class ItemsTooltipLineHandler implements ITooltipLineHandler { GL11.glTranslatef(x, y + fontRenderer.FONT_HEIGHT + 2, 0); GL11.glEnable(GL12.GL_RESCALE_NORMAL); - for (int i = 0; i < this.count; i++) { - ItemStack drawStack = this.items.get(i); - int col = i % this.columns; - int row = i / this.columns; + int indexShift = 0; + + if (this.activeStackIndex != -1) { + indexShift = Math.max(0, Math.min(this.items.size() - this.count, this.activeStackIndex - this.count + 2)); + } + + 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); - drawItem(col * ItemsGrid.SLOT_SIZE, row * ItemsGrid.SLOT_SIZE, drawStack, stackSize); + if (this.activeStackIndex == index + indexShift) { + NEIClientUtils.gl2DRenderContext( + () -> GuiDraw.drawRect(col * SLOT_SIZE - 1, row * SLOT_SIZE - 1, 18, 18, 0x66555555)); + } + + drawItem(col * SLOT_SIZE, row * SLOT_SIZE, drawStack, stackSize); } if (this.count < this.items.size()) { - String text = "+" + (this.items.size() - this.count); - - fontRenderer.drawStringWithShadow( - text, - MAX_COLUMNS * ItemsGrid.SLOT_SIZE - fontRenderer.getStringWidth(text) - 2, - (this.rows - 1) * ItemsGrid.SLOT_SIZE + (ItemsGrid.SLOT_SIZE - fontRenderer.FONT_HEIGHT) / 2, - 0xee555555); + final String text = "+" + (this.items.size() - this.count); + NEIClientUtils.gl2DRenderContext(() -> { + fontRenderer.drawStringWithShadow( + text, + MAX_COLUMNS * SLOT_SIZE - fontRenderer.getStringWidth(text) - 2, + (this.rows - 1) * SLOT_SIZE + (SLOT_SIZE - fontRenderer.FONT_HEIGHT) / 2, + 0xee555555); + }); } GL11.glPopMatrix(); @@ -124,12 +151,19 @@ public class ItemsTooltipLineHandler implements ITooltipLineHandler { } private List groupingItemStacks(List items) { - final List result = ItemStackAmount.of(items).values(); + final List result = new ArrayList<>(); - for (ItemStack stack : result) { - if (StackInfo.itemStackToNBT(stack).hasKey("gtFluidName")) { + for (Map.Entry entry : ItemStackAmount.of(items).entrySet()) { + final ItemStack stack = StackInfo.loadFromNBT(entry.getKey(), Math.max(0, entry.getValue())); + + if (entry.getKey().hasKey("gtFluidName")) { stack.stackSize = 0; } + result.add(stack); + } + + if (result.isEmpty()) { + result.addAll(items); } return result; diff --git a/src/main/java/codechicken/nei/LayoutManager.java b/src/main/java/codechicken/nei/LayoutManager.java index 3600e48c..ed284a99 100644 --- a/src/main/java/codechicken/nei/LayoutManager.java +++ b/src/main/java/codechicken/nei/LayoutManager.java @@ -65,6 +65,7 @@ import codechicken.nei.guihook.IContainerDrawHandler; import codechicken.nei.guihook.IContainerInputHandler; import codechicken.nei.guihook.IContainerObjectHandler; import codechicken.nei.guihook.IContainerTooltipHandler; +import codechicken.nei.recipe.AutoCraftingManager; import codechicken.nei.recipe.GuiRecipeTab; import codechicken.nei.recipe.RecipeCatalysts; @@ -829,7 +830,7 @@ public class LayoutManager implements IContainerInputHandler, IContainerTooltipH @Override public boolean shouldShowTooltip(GuiContainer gui) { - return itemPanel.draggedStack == null && !bookmarkPanel.inEditingState(); + return itemPanel.draggedStack == null && !bookmarkPanel.inEditingState() && !AutoCraftingManager.processing(); } /** Note: this method isn't actually used by this mod, but NEI add-ons might need it. */ @@ -857,13 +858,11 @@ public class LayoutManager implements IContainerInputHandler, IContainerTooltipH ItemStack item = slot.getStack(); if (item == null ? !getSearchExpression().equals("") : !searchField.getFilter().matches(item)) { - GL11.glDisable(GL11.GL_LIGHTING); - GL11.glDisable(GL11.GL_DEPTH_TEST); - GL11.glTranslatef(0, 0, 150); - drawRect(slot.xDisplayPosition, slot.yDisplayPosition, 16, 16, 0x80000000); - GL11.glTranslatef(0, 0, -150); - GL11.glEnable(GL11.GL_LIGHTING); - GL11.glEnable(GL11.GL_DEPTH_TEST); + NEIClientUtils.gl2DRenderContext(() -> { + GL11.glTranslatef(0, 0, 150); + drawRect(slot.xDisplayPosition, slot.yDisplayPosition, 16, 16, 0x80000000); + GL11.glTranslatef(0, 0, -150); + }); } } @@ -925,11 +924,7 @@ public class LayoutManager implements IContainerInputHandler, IContainerTooltipH public static void drawItemPresenceOverlay(int slotX, int slotY, boolean isPresent, boolean slotHighlight) { if (slotHighlight) { - GL11.glPushAttrib(GL11.GL_ALL_ATTRIB_BITS); - GL11.glDisable(GL11.GL_LIGHTING); - GL11.glDisable(GL11.GL_DEPTH_TEST); - drawRect(slotX, slotY, 16, 16, isPresent ? 0x8000AA00 : 0x80AA0000); - GL11.glPopAttrib(); + NEIClientUtils.gl2DRenderContext(() -> drawRect(slotX, slotY, 16, 16, isPresent ? 0x8000AA00 : 0x80AA0000)); } else { Image icon = itemPresenceOverlays[isPresent ? 1 : 0]; drawIcon(slotX + 16 - icon.width, slotY + 16 - icon.height, icon); diff --git a/src/main/java/codechicken/nei/NEIClientConfig.java b/src/main/java/codechicken/nei/NEIClientConfig.java index 352566e5..b066892d 100644 --- a/src/main/java/codechicken/nei/NEIClientConfig.java +++ b/src/main/java/codechicken/nei/NEIClientConfig.java @@ -146,6 +146,7 @@ public class NEIClientConfig { tag.getTag("inventory.widgetsenabled").getBooleanValue(true); API.addOption(new OptionToggleButton("inventory.widgetsenabled")); + tag.getTag("inventory.autocrafting").getBooleanValue(false); tag.getTag("inventory.hidden").getBooleanValue(false); tag.getTag("inventory.cheatmode").getIntValue(2); tag.getTag("inventory.lockmode").setComment( @@ -179,6 +180,7 @@ public class NEIClientConfig { @Override public boolean onClick(int button) { super.onClick(button); + ItemPanels.bookmarkPanel.save(); ItemPanels.bookmarkPanel.load(); return true; } @@ -192,11 +194,19 @@ public class NEIClientConfig { .getIntValue(1); API.addOption(new OptionCycled("inventory.bookmarks.recipeTooltipsMode", 4, true)); - tag.getTag("inventory.bookmarks.showRecipeMarker").setComment("Show Recipe Marker").getBooleanValue(false); - API.addOption(new OptionToggleButton("inventory.bookmarks.showRecipeMarker", true)); + tag.getTag("inventory.bookmarks.showRecipeMarkerMode").setComment("Show Recipe Marker").getIntValue(0); + API.addOption(new OptionCycled("inventory.bookmarks.showRecipeMarkerMode", 3, true)); - tag.getTag("inventory.bookmarks.craftingChainDir").getIntValue(1); - API.addOption(new OptionCycled("inventory.bookmarks.craftingChainDir", 2, true)); + tag.getTag("inventory.bookmarks.recipeMarkerColor").setComment("Color of the icon marker") + .getHexValue(0xADADAD); + API.addOption( + new OptionIntegerField( + "inventory.bookmarks.recipeMarkerColor", + 0, + OptionIntegerField.UNSIGNED_INT_MAX)); + + tag.getTag("inventory.bookmarks.recipeChainDir").getIntValue(1); + API.addOption(new OptionCycled("inventory.bookmarks.recipeChainDir", 2, true)); tag.getTag("inventory.bookmarks.ignorePotionOverlap").setComment("Ignore overlap with potion effect HUD") .getBooleanValue(false); @@ -234,15 +244,15 @@ public class NEIClientConfig { .setComment("Require holding shift to move items when overlaying recipe").getBooleanValue(true); API.addOption(new OptionToggleButton("inventory.guirecipe.shiftOverlayRecipe", true)); + tag.getTag("inventory.guirecipe.profile").getBooleanValue(false); + API.addOption(new OptionToggleButton("inventory.guirecipe.profile", true)); + tag.getTag("inventory.subsets.enabled").setComment("Enable/disable Subsets Dropdown").getBooleanValue(true); API.addOption(new OptionToggleButton("inventory.subsets.enabled", true)); tag.getTag("inventory.subsets.widgetPosition").setComment("Subsets Widget Position").getBooleanValue(true); API.addOption(new OptionToggleButton("inventory.subsets.widgetPosition", true)); - tag.getTag("inventory.guirecipe.profile").getBooleanValue(false); - API.addOption(new OptionToggleButton("inventory.guirecipe.profile", true)); - tag.getTag("inventory.history.enabled").setComment("Enable/disable History Panel").getBooleanValue(true); API.addOption(new OptionToggleButton("inventory.history.enabled", true)); @@ -313,6 +323,8 @@ public class NEIClientConfig { tag.getTag("inventory.itemzoom.nameColor").getHexValue(0xFFFFFFFF); API.addOption(new OptionIntegerField("inventory.itemzoom.nameColor", 0, OptionIntegerField.UNSIGNED_INT_MAX)); + setFavoriteDefaults(tag); + tag.getTag("inventory.itemIDs").getIntValue(1); API.addOption(new OptionCycled("inventory.itemIDs", 3, true)); @@ -338,6 +350,16 @@ public class NEIClientConfig { tag.getTag("world.overlays.lock").getBooleanValue(true); API.addOption(new OptionToggleButton("world.overlays.lock", true)); + tag.getTag("inventory.itempanelScale").getIntValue(100); + ItemsPanelGrid.updateScale(); + API.addOption(new OptionIntegerField("inventory.itempanelScale", 1, 1000) { + + @Override + public void onTextChange(String text) { + ItemsPanelGrid.updateScale(); + } + }); + tag.getTag("inventory.disableMouseScrollTransfer").getBooleanValue(false); API.addOption(new OptionToggleButton("inventory.disableMouseScrollTransfer", true)); @@ -350,8 +372,6 @@ public class NEIClientConfig { } }); - tag.getTag("inventory.gridRenderingCacheFPS").getIntValue(8); - tag.getTag("inventory.gridRenderingCacheMode").getIntValue(0); API.addOption(new OptionCycled("inventory.gridRenderingCacheMode", 3, true)); @@ -671,6 +691,36 @@ public class NEIClientConfig { } + private static void setFavoriteDefaults(ConfigTagParent tag) { + + tag.getTag("inventory.favorites.enabled").getBooleanValue(true); + API.addOption(new OptionToggleButton("inventory.favorites.enabled", true)); + + tag.getTag("inventory.favorites.worldSpecific").setComment("Global or world specific favorites") + .getBooleanValue(false); + API.addOption(new OptionToggleButton("inventory.favorites.worldSpecific", true) { + + @Override + public boolean onClick(int button) { + super.onClick(button); + FavoriteRecipes.save(); + FavoriteRecipes.load(); + return true; + } + }); + + tag.getTag("inventory.favorites.showRecipeTooltipInPanel").setComment("Show recipe tooltips in Items Panel") + .getBooleanValue(true); + API.addOption(new OptionToggleButton("inventory.favorites.showRecipeTooltipInPanel", true)); + + tag.getTag("inventory.favorites.showRecipeTooltipInGui").setComment("Show recipe tooltips in Recipe Gui") + .getBooleanValue(false); + API.addOption(new OptionToggleButton("inventory.favorites.showRecipeTooltipInGui", true)); + + tag.getTag("inventory.favorites.depth").setComment("Bookmark creation depth").getIntValue(3); + API.addOption(new OptionIntegerField("inventory.favorites.depth", 0, 100)); + } + private static void setDefaultKeyBindings() { API.addHashBind("gui.recipe", Keyboard.KEY_R); API.addHashBind("gui.usage", Keyboard.KEY_U); @@ -685,22 +735,15 @@ public class NEIClientConfig { API.addKeyBind("gui.next_recipe", Keyboard.KEY_RIGHT); API.addHashBind("gui.hide", Keyboard.KEY_O); API.addHashBind("gui.search", Keyboard.KEY_F); - API.addHashBind("gui.bookmark", Keyboard.KEY_A); - API.addHashBind("gui.bookmark_recipe", Keyboard.KEY_A + NEIClientUtils.SHIFT_HASH); + API.addKeyBind("gui.bookmark", Keyboard.KEY_A); API.addHashBind("gui.remove_recipe", Keyboard.KEY_A + NEIClientUtils.SHIFT_HASH); - API.addHashBind("gui.bookmark_count", Keyboard.KEY_A + NEIClientUtils.CTRL_HASH); - API.addHashBind( - "gui.bookmark_recipe_count", - Keyboard.KEY_A + NEIClientUtils.SHIFT_HASH + NEIClientUtils.CTRL_HASH); - API.addHashBind("gui.bookmark_pull_items", Keyboard.KEY_V); - API.addHashBind("gui.bookmark_pull_items_ingredients", Keyboard.KEY_V + NEIClientUtils.SHIFT_HASH); - API.addHashBind("gui.overlay", Keyboard.KEY_S); - API.addHashBind("gui.overlay_use", Keyboard.KEY_S + NEIClientUtils.SHIFT_HASH); - API.addHashBind("gui.overlay_hide", Keyboard.KEY_S + NEIClientUtils.CTRL_HASH); + API.addKeyBind("gui.bookmark_pull_items", Keyboard.KEY_V); + API.addKeyBind("gui.overlay", Keyboard.KEY_S); + API.addHashBind("gui.craft", Keyboard.KEY_C + NEIClientUtils.SHIFT_HASH); API.addHashBind("gui.hide_bookmarks", Keyboard.KEY_B); API.addKeyBind("gui.getprevioussearch", Keyboard.KEY_UP); API.addKeyBind("gui.getnextsearch", Keyboard.KEY_DOWN); - API.addHashBind("gui.next_tooltip", Keyboard.KEY_Z); + API.addKeyBind("gui.next_tooltip", Keyboard.KEY_Z); API.addHashBind("gui.itemzoom_toggle", Keyboard.KEY_Z + NEIClientUtils.SHIFT_HASH); API.addHashBind("gui.itemzoom_hold", 0); @@ -779,10 +822,11 @@ public class NEIClientConfig { public static void unloadWorld() { if (ItemPanels.bookmarkPanel != null) { - ItemPanels.bookmarkPanel.saveBookmarks(); + ItemPanels.bookmarkPanel.save(); } SubsetWidget.saveHidden(); + FavoriteRecipes.save(); CollapsibleItems.saveStates(); if (world != null) { @@ -837,6 +881,7 @@ public class NEIClientConfig { } public static void bootNEI(World world) { + if (!mainNEIConfigLoaded) { // main NEI config loading ItemInfo.load(world); @@ -868,10 +913,10 @@ public class NEIClientConfig { }); RecipeCatalysts.loadCatalystInfo(); - ItemPanels.bookmarkPanel.load(); SubsetWidget.loadHidden(); CollapsibleItems.load(); ItemSorter.loadConfig(); + FavoriteRecipes.load(); // Set pluginNEIConfigLoaded here before posting the NEIConfigsLoadedEvent. This used to be the // other way around, but apparently if your modpack includes 800 mods the event poster might not @@ -885,10 +930,10 @@ public class NEIClientConfig { } }.start(); } else { - ItemPanels.bookmarkPanel.load(); SubsetWidget.loadHidden(); ItemList.loadItems.restart(); } + } public static boolean isWorldSpecific(String setting) { @@ -909,6 +954,14 @@ public class NEIClientConfig { return !enabledOverride || getBooleanSetting("inventory.hidden"); } + public static boolean autocraftingEnabled() { + return getBooleanSetting("inventory.autocrafting"); + } + + public static boolean favoritesEnabled() { + return getBooleanSetting("inventory.favorites.enabled"); + } + public static boolean isBookmarkPanelHidden() { return !getBooleanSetting("inventory.bookmarks.enabled"); } @@ -921,8 +974,16 @@ public class NEIClientConfig { return getIntSetting("inventory.bookmarks.recipeTooltipsMode"); } - public static boolean showRecipeMarker() { - return getBooleanSetting("inventory.bookmarks.showRecipeMarker"); + public static boolean showRecipeTooltipInPanel() { + return getBooleanSetting("inventory.favorites.showRecipeTooltipInPanel"); + } + + public static boolean showRecipeTooltipInGui() { + return getBooleanSetting("inventory.favorites.showRecipeTooltipInGui"); + } + + public static int showRecipeMarkerMode() { + return getIntSetting("inventory.bookmarks.showRecipeMarkerMode"); } public static boolean showItemQuantityWidget() { @@ -973,8 +1034,8 @@ public class NEIClientConfig { return getBooleanSetting("inventory.bookmarks.ignorePotionOverlap"); } - public static int craftingChainDir() { - return getIntSetting("inventory.bookmarks.craftingChainDir"); + public static int recipeChainDir() { + return getIntSetting("inventory.bookmarks.recipeChainDir"); } public static boolean optimizeGuiOverlapComputation() { diff --git a/src/main/java/codechicken/nei/NEIClientUtils.java b/src/main/java/codechicken/nei/NEIClientUtils.java index 00ae63be..3edc4748 100644 --- a/src/main/java/codechicken/nei/NEIClientUtils.java +++ b/src/main/java/codechicken/nei/NEIClientUtils.java @@ -8,6 +8,7 @@ import static codechicken.nei.NEIClientConfig.hasSMPCounterPart; import static codechicken.nei.NEIClientConfig.invCreativeMode; import static codechicken.nei.NEIClientConfig.world; +import java.awt.Color; import java.io.PrintWriter; import java.io.StringWriter; import java.math.BigInteger; @@ -32,6 +33,8 @@ import net.minecraft.client.Minecraft; import net.minecraft.client.audio.PositionedSoundRecord; import net.minecraft.client.gui.FontRenderer; import net.minecraft.client.gui.inventory.GuiContainer; +import net.minecraft.client.renderer.OpenGlHelper; +import net.minecraft.client.renderer.Tessellator; import net.minecraft.entity.player.EntityPlayer; import net.minecraft.inventory.Container; import net.minecraft.inventory.Slot; @@ -41,12 +44,15 @@ import net.minecraft.util.IChatComponent; import net.minecraft.util.ResourceLocation; import org.lwjgl.input.Keyboard; +import org.lwjgl.opengl.GL11; import com.google.common.collect.Iterables; +import codechicken.lib.gui.GuiDraw; import codechicken.lib.inventory.InventoryRange; import codechicken.lib.inventory.InventoryUtils; import codechicken.lib.util.LangProxy; +import codechicken.lib.vec.Rectangle4i; import codechicken.nei.api.GuiInfo; import codechicken.nei.api.IInfiniteItemHandler; import codechicken.nei.api.ItemInfo; @@ -55,6 +61,36 @@ import codechicken.nei.util.NEIMouseUtils; public class NEIClientUtils extends NEIServerUtils { + public static class Alignment { + + public final int x, y; + + public static final Alignment TopLeft = new Alignment(-1, -1); + public static final Alignment TopCenter = new Alignment(0, -1); + public static final Alignment TopRight = new Alignment(1, -1); + public static final Alignment CenterLeft = new Alignment(-1, 0); + public static final Alignment Center = new Alignment(0, 0); + public static final Alignment CenterRight = new Alignment(1, 0); + public static final Alignment BottomLeft = new Alignment(-1, 1); + public static final Alignment BottomCenter = new Alignment(0, 1); + public static final Alignment BottomRight = new Alignment(1, 1); + + public Alignment(int x, int y) { + this.x = x; + this.y = y; + } + + public float getX(float parentWidth, float childWidth) { + final float x = (this.x + 1) / 2f; + return parentWidth * x - childWidth * x; + } + + public float getY(float parentHeight, float childHeight) { + final float y = (this.y + 1) / 2f; + return parentHeight * y - childHeight * y; + } + } + public static LangProxy lang = new LangProxy("nei"); /** Formats a number with group separator and at most 2 fraction digits. */ @@ -486,4 +522,66 @@ public class NEIClientUtils extends NEIServerUtils { reportErrorBuffered(e, buffer, "null"); } } + + public static void drawRect(double left, double top, double width, double height, Color color) { + Tessellator tessellator = Tessellator.instance; + + GL11.glEnable(GL11.GL_BLEND); + GL11.glDisable(GL11.GL_TEXTURE_2D); + OpenGlHelper.glBlendFunc(770, 771, 1, 0); + GL11.glColor4f(color.getRed() / 255f, color.getGreen() / 255f, color.getBlue() / 255f, color.getAlpha() / 255f); + tessellator.startDrawingQuads(); + tessellator.addVertex(left, top + height, 0.0D); + tessellator.addVertex(left + width, top + height, 0.0D); + tessellator.addVertex(left + width, top, 0.0D); + tessellator.addVertex(left, top, 0.0D); + tessellator.draw(); + GL11.glEnable(GL11.GL_TEXTURE_2D); + GL11.glDisable(GL11.GL_BLEND); + } + + public static void drawNEIOverlayText(String text, Rectangle4i rect, float scale, int color, boolean shadow, + Alignment alignment) { + final float screenScale = mc().currentScreen.width * 1f / mc().displayWidth; + final double smallTextScale = Math + .max(screenScale, (Math.max(scale, 1f) * (GuiDraw.fontRenderer.getUnicodeFlag() ? 3F / 4F : 1F / 2F))); + + NEIClientUtils.gl2DRenderContext(() -> { + final int width = GuiDraw.fontRenderer.getStringWidth(text); + final float partW = rect.w / 2f; + final float partH = rect.h / 2f; + final double offsetX = Math + .ceil(rect.x + partW + partW * alignment.x - (width / 2f * (alignment.x + 1)) * smallTextScale); + final double offsetY = Math.ceil( + rect.y + partH + + partH * alignment.y + - (GuiDraw.fontRenderer.FONT_HEIGHT / 2f * (alignment.y + 1)) * smallTextScale); + + GL11.glTranslated(offsetX, offsetY, 0); + GL11.glScaled(smallTextScale, smallTextScale, 1); + GuiDraw.fontRenderer.drawString(text, 0, 0, color, shadow); + GL11.glScaled(1 / smallTextScale, 1 / smallTextScale, 1); + GL11.glTranslated(-1 * offsetX, -1 * offsetY, 0); + }); + } + + public static void drawNEIOverlayText(String text, int x, int y) { + drawNEIOverlayText(text, new Rectangle4i(x, y, 16, 16), 0.5f, 0xFDD835, false, Alignment.TopLeft); + } + + public static void gl2DRenderContext(Runnable callback) { + boolean isLighting = GL11.glGetBoolean(GL11.GL_LIGHTING); + boolean isDepthTest = GL11.glGetBoolean(GL11.GL_DEPTH_TEST); + boolean isAlphaTest = GL11.glGetBoolean(GL11.GL_ALPHA_TEST); + GL11.glDisable(GL11.GL_LIGHTING); + GL11.glEnable(GL11.GL_ALPHA_TEST); + GL11.glDisable(GL11.GL_DEPTH_TEST); + + callback.run(); + + if (isLighting) GL11.glEnable(GL11.GL_LIGHTING); + if (isDepthTest) GL11.glEnable(GL11.GL_DEPTH_TEST); + if (!isAlphaTest) GL11.glDisable(GL11.GL_ALPHA_TEST); + } + } diff --git a/src/main/java/codechicken/nei/PanelWidget.java b/src/main/java/codechicken/nei/PanelWidget.java index 0674ae87..aab9aceb 100644 --- a/src/main/java/codechicken/nei/PanelWidget.java +++ b/src/main/java/codechicken/nei/PanelWidget.java @@ -1,13 +1,14 @@ package codechicken.nei; +import java.awt.Point; import java.util.ArrayList; -import java.util.List; import net.minecraft.client.gui.inventory.GuiContainer; import net.minecraft.item.ItemStack; +import codechicken.lib.gui.GuiDraw; import codechicken.lib.vec.Rectangle4i; -import codechicken.nei.ItemPanel.ItemPanelSlot; +import codechicken.nei.ItemsGrid.ItemsGridSlot; import codechicken.nei.api.GuiInfo; import codechicken.nei.api.INEIGuiHandler; import codechicken.nei.guihook.GuiContainerManager; @@ -16,7 +17,8 @@ import codechicken.nei.recipe.GuiCraftingRecipe; import codechicken.nei.recipe.GuiRecipe; import codechicken.nei.recipe.GuiUsageRecipe; -public abstract class PanelWidget extends Widget implements IContainerTooltipHandler { +public abstract class PanelWidget> + extends Widget implements IContainerTooltipHandler { protected static final int PADDING = 2; @@ -27,20 +29,20 @@ public abstract class PanelWidget extends Widget implements IContainerTooltipHan public Button pageNext; public Label pageLabel; - protected ItemsGrid grid; + protected T grid; public ArrayList getItems() { - return grid.getItems(); + return this.grid.getItems(); } - public ItemsGrid getGrid() { - return grid; + public T getGrid() { + return this.grid; } public void init() { pageLabel = new Label("0/0", true); - pagePrev = new Button("Prev") { + pagePrev = new Button("<") { public boolean onButtonPress(boolean rightclick) { @@ -54,11 +56,12 @@ public abstract class PanelWidget extends Widget implements IContainerTooltipHan } @Override - public String getRenderLabel() { - return "<"; + public void draw(int mousex, int mousey) { + this.state = grid.getNumPages() <= 1 ? 2 : 0; + super.draw(mousex, mousey); } }; - pageNext = new Button("Next") { + pageNext = new Button(">") { public boolean onButtonPress(boolean rightclick) { @@ -72,8 +75,9 @@ public abstract class PanelWidget extends Widget implements IContainerTooltipHan } @Override - public String getRenderLabel() { - return ">"; + public void draw(int mousex, int mousey) { + this.state = grid.getNumPages() <= 1 ? 2 : 0; + super.draw(mousex, mousey); } }; } @@ -97,9 +101,6 @@ public abstract class PanelWidget extends Widget implements IContainerTooltipHan getWidth(gui), getHeight(gui)); - final int minWidth = 5 * ItemsGrid.SLOT_SIZE; - final int minHeight = 9 * ItemsGrid.SLOT_SIZE; - final String settingName = getPositioningSettingName(); int paddingLeft = (int) Math .ceil(margin.w * NEIClientConfig.getSetting(settingName + ".left").getIntValue() / 100000.0); @@ -110,12 +111,12 @@ public abstract class PanelWidget extends Widget implements IContainerTooltipHan int paddingBottom = (int) Math .ceil(margin.h * NEIClientConfig.getSetting(settingName + ".bottom").getIntValue() / 100000.0); - int deltaHeight = Math.min(0, margin.h - paddingTop - paddingBottom - minHeight) / 2; + int deltaHeight = Math.min(0, margin.h - paddingTop - paddingBottom - ItemsGrid.SLOT_SIZE) / 2; - paddingLeft = Math.min(paddingLeft, Math.max(0, margin.w - paddingRight - minWidth)); - paddingRight = Math.min(paddingRight, Math.max(0, margin.w - paddingLeft - minWidth)); - paddingTop = Math.min(margin.h - minHeight, Math.max(0, paddingTop + deltaHeight)); - paddingBottom = Math.min(margin.h - paddingTop - minHeight, Math.max(0, paddingBottom - deltaHeight)); + paddingLeft = Math.min(paddingLeft, Math.max(0, margin.w - paddingRight - ItemsGrid.SLOT_SIZE)); + paddingRight = Math.min(paddingRight, Math.max(0, margin.w - paddingLeft - ItemsGrid.SLOT_SIZE)); + paddingTop = Math.min(margin.h - ItemsGrid.SLOT_SIZE, Math.max(0, paddingTop + deltaHeight)); + paddingBottom = Math.min(margin.h - paddingTop - ItemsGrid.SLOT_SIZE, Math.max(0, paddingBottom - deltaHeight)); x = margin.x + paddingLeft; y = margin.y + paddingTop; @@ -125,8 +126,8 @@ public abstract class PanelWidget extends Widget implements IContainerTooltipHan final int header = resizeHeader(gui); final int footer = resizeFooter(gui); - grid.setGridSize(x, y + header, w, h - header - footer); - grid.refresh(gui); + this.grid.setGridSize(x, y + header, w, h - header - footer); + this.grid.refresh(gui); } protected int resizeHeader(GuiContainer gui) { @@ -171,22 +172,22 @@ public abstract class PanelWidget extends Widget implements IContainerTooltipHan protected abstract int resizeFooter(GuiContainer gui); public void setVisible() { - if (grid.getPerPage() > 0 && !grid.isEmpty()) { + if (this.grid.getPerPage() > 0 && !this.grid.isEmpty()) { LayoutManager.addWidget(pagePrev); LayoutManager.addWidget(pageNext); LayoutManager.addWidget(pageLabel); - grid.setVisible(); + this.grid.setVisible(); } } @Override public void update() { - grid.update(); + this.grid.update(); } @Override public void draw(int mousex, int mousey) { - grid.draw(mousex, mousey); + this.grid.draw(mousex, mousey); } @Override @@ -200,37 +201,33 @@ public abstract class PanelWidget extends Widget implements IContainerTooltipHan @Override public void mouseDragged(int mousex, int mousey, int button, long heldTime) { - if (mouseDownSlot >= grid.realItems.size()) { + if (this.grid.getSlotBySlotIndex(mouseDownSlot) == null) { mouseDownSlot = -1; } if (mouseDownSlot >= 0 && draggedStack == null && NEIClientUtils.getHeldItem() == null && NEIClientConfig.hasSMPCounterPart()) { - ItemPanelSlot mouseOverSlot = getSlotMouseOver(mousex, mousey); + ItemsGridSlot mouseOverSlot = this.grid.getSlotMouseOver(mousex, mousey); if (mouseOverSlot == null || mouseOverSlot.slotIndex != mouseDownSlot || heldTime > 500) { - draggedStack = getDraggedStackWithQuantity(mouseDownSlot); + draggedStack = getDraggedStackWithQuantity(this.grid.getSlotBySlotIndex(mouseDownSlot).getItemStack()); mouseDownSlot = -1; } } } - protected abstract ItemStack getDraggedStackWithQuantity(int mouseDownSlot); + protected abstract ItemStack getDraggedStackWithQuantity(ItemStack itemStack); @Override public boolean handleClick(int mousex, int mousey, int button) { if (handleClickExt(mousex, mousey, button)) return true; - ItemPanelSlot hoverSlot = getSlotMouseOver(mousex, mousey); + ItemsGridSlot hoverSlot = this.grid.getSlotMouseOver(mousex, mousey); if (hoverSlot != null) { if (button == 2) { - - if (hoverSlot.item != null) { - draggedStack = getDraggedStackWithQuantity(hoverSlot.slotIndex); - } - + draggedStack = getDraggedStackWithQuantity(hoverSlot.getItemStack()); } else { mouseDownSlot = hoverSlot.slotIndex; } @@ -245,7 +242,7 @@ public abstract class PanelWidget extends Widget implements IContainerTooltipHan public boolean handleClickExt(int mouseX, int mouseY, int button) { if (ItemPanels.itemPanel.draggedStack != null && ItemPanels.bookmarkPanel.contains(mouseX, mouseY)) { - ItemPanels.bookmarkPanel.addOrRemoveItem(ItemPanels.itemPanel.draggedStack, null, null, false, true); + ItemPanels.bookmarkPanel.addItem(ItemPanels.itemPanel.draggedStack); ItemPanels.itemPanel.draggedStack = null; return true; } @@ -311,25 +308,23 @@ public abstract class PanelWidget extends Widget implements IContainerTooltipHan @Override public void mouseUp(int mousex, int mousey, int button) { - ItemPanelSlot hoverSlot = getSlotMouseOver(mousex, mousey); + final ItemsGridSlot hoverSlot = this.grid.getSlotMouseOver(mousex, mousey); if (hoverSlot != null && hoverSlot.slotIndex == mouseDownSlot && draggedStack == null) { - ItemStack item = hoverSlot.item.copy(); if (NEIController.manager.window instanceof GuiRecipe || NEIClientUtils.shiftKey() - || !NEIClientConfig.canCheatItem(item)) { + || !NEIClientConfig.canCheatItem(hoverSlot.getItemStack())) { if (button == 0) { - GuiCraftingRecipe.openRecipeGui("item", item); + GuiCraftingRecipe.openRecipeGui("item", hoverSlot.getItemStack().copy()); } else if (button == 1) { - GuiUsageRecipe.openRecipeGui("item", item); + GuiUsageRecipe.openRecipeGui("item", hoverSlot.getItemStack().copy()); } - mouseDownSlot = -1; - return; + } else { + NEIClientUtils.cheatItem(getDraggedStackWithQuantity(hoverSlot.getItemStack()), button, -1); } - NEIClientUtils.cheatItem(getDraggedStackWithQuantity(hoverSlot.slotIndex), button, -1); } mouseDownSlot = -1; @@ -339,20 +334,22 @@ public abstract class PanelWidget extends Widget implements IContainerTooltipHan public boolean onMouseWheel(int i, int mousex, int mousey) { if (!contains(mousex, mousey)) return false; - grid.shiftPage(-i); + this.grid.shiftPage(-i); return true; } @Override public boolean handleKeyPress(int keyID, char keyChar) { + final Point mouse = GuiDraw.getMousePosition(); + if (!contains(mouse.x, mouse.y)) return false; if (NEIClientConfig.isKeyHashDown("gui.next")) { - grid.shiftPage(1); + this.grid.shiftPage(1); return true; } if (NEIClientConfig.isKeyHashDown("gui.prev")) { - grid.shiftPage(-1); + this.grid.shiftPage(-1); return true; } @@ -361,41 +358,28 @@ public abstract class PanelWidget extends Widget implements IContainerTooltipHan @Override public ItemStack getStackMouseOver(int mousex, int mousey) { - ItemPanelSlot slot = getSlotMouseOver(mousex, mousey); - return slot == null ? null : slot.item; + final ItemsGridSlot slot = this.grid.getSlotMouseOver(mousex, mousey); + return slot == null ? null : slot.getItemStack(); } - public ItemPanelSlot getSlotMouseOver(int mousex, int mousey) { - return grid.getSlotMouseOver(mousex, mousey); + public ItemStack getStackMouseOverWithQuantity(int mousex, int mousey) { + final ItemStack hoverSlot = getStackMouseOver(mousex, mousey); + return hoverSlot != null ? getDraggedStackWithQuantity(hoverSlot) : null; } + abstract ItemsGridSlot getSlotMouseOver(int mousex, int mousey); + public int getPage() { - return grid.getPage(); + return this.grid.getPage(); } public int getNumPages() { - return grid.getNumPages(); + return this.grid.getNumPages(); } @Override public boolean contains(int px, int py) { - return grid.contains(px, py); - } - - @Override - public List handleTooltip(GuiContainer gui, int mousex, int mousey, List currenttip) { - return currenttip; - } - - @Override - public List handleItemDisplayName(GuiContainer gui, ItemStack itemstack, List currenttip) { - return currenttip; - } - - @Override - public List handleItemTooltip(GuiContainer gui, ItemStack itemstack, int mousex, int mousey, - List currenttip) { - return currenttip; + return this.grid.contains(px, py); } } diff --git a/src/main/java/codechicken/nei/PositionedStack.java b/src/main/java/codechicken/nei/PositionedStack.java index efbddeb9..9c1b456a 100644 --- a/src/main/java/codechicken/nei/PositionedStack.java +++ b/src/main/java/codechicken/nei/PositionedStack.java @@ -2,6 +2,7 @@ package codechicken.nei; import java.util.ArrayList; import java.util.Arrays; +import java.util.Comparator; import java.util.List; import java.util.stream.Collectors; @@ -10,7 +11,6 @@ import net.minecraft.item.Item; import net.minecraft.item.ItemStack; import net.minecraftforge.oredict.OreDictionary; -import codechicken.nei.ItemList.AllMultiItemFilter; import codechicken.nei.api.ItemFilter; import codechicken.nei.api.ItemInfo; import codechicken.nei.recipe.GuiRecipe; @@ -96,28 +96,21 @@ public class PositionedStack { } public List getFilteredPermutations(ItemFilter additionalFilter) { - List items = Arrays.asList(this.items).stream().filter(getItemFilter(additionalFilter)::matches) - .collect(Collectors.toList()); + List items = Arrays.asList(this.items); - if (items.isEmpty()) { - items.addAll( - Arrays.asList(this.items).stream().filter(item -> !ItemInfo.isHidden(item)) - .collect(Collectors.toList())); - } - - if (items.isEmpty()) { - items.addAll(Arrays.asList(this.items)); - } + items = filteringPermutations(items, item -> !ItemInfo.isHidden(item)); + items = filteringPermutations(items, PresetsList.getItemFilter()); + items = filteringPermutations(items, GuiRecipe.getSearchItemFilter()); + items = filteringPermutations(items, additionalFilter); + items.sort(Comparator.comparing(FavoriteRecipes::contains).reversed()); return items; } - private ItemFilter getItemFilter(ItemFilter additionalFilter) { - return new AllMultiItemFilter( - additionalFilter, - item -> !ItemInfo.isHidden(item), - PresetsList.getItemFilter(), - GuiRecipe.getSearchItemFilter()); + private List filteringPermutations(List items, ItemFilter filter) { + if (filter == null) return items; + final List filteredItems = items.stream().filter(filter::matches).collect(Collectors.toList()); + return filteredItems.isEmpty() ? items : filteredItems; } public boolean setPermutationToRender(ItemStack ingredient) { diff --git a/src/main/java/codechicken/nei/SearchField.java b/src/main/java/codechicken/nei/SearchField.java index d5a4cf90..608b2781 100644 --- a/src/main/java/codechicken/nei/SearchField.java +++ b/src/main/java/codechicken/nei/SearchField.java @@ -281,7 +281,7 @@ public class SearchField extends TextField implements ItemFilterProvider { } public static String getEscapedSearchText(ItemStack stack) { - final FluidStack fluidStack = StackInfo.isFluidContainer(stack) ? null : StackInfo.getFluid(stack); + final FluidStack fluidStack = StackInfo.getFluid(stack); String displayName; if (fluidStack != null) { diff --git a/src/main/java/codechicken/nei/SearchTokenParser.java b/src/main/java/codechicken/nei/SearchTokenParser.java index fdb7170c..3b259751 100644 --- a/src/main/java/codechicken/nei/SearchTokenParser.java +++ b/src/main/java/codechicken/nei/SearchTokenParser.java @@ -158,7 +158,7 @@ public class SearchTokenParser { return this.filtersCache.computeIfAbsent(filterText, text -> { final String[] parts = text.split("\\|"); final List searchTokens = Arrays.stream(parts).map(this::parseSearchText).filter(s -> s != null) - .collect(Collectors.toCollection(ArrayList::new)); + .collect(Collectors.toList()); if (searchTokens.isEmpty()) { return new EverythingItemFilter(); diff --git a/src/main/java/codechicken/nei/api/DefaultOverlayRenderer.java b/src/main/java/codechicken/nei/api/DefaultOverlayRenderer.java index 04f44b0a..d317e187 100644 --- a/src/main/java/codechicken/nei/api/DefaultOverlayRenderer.java +++ b/src/main/java/codechicken/nei/api/DefaultOverlayRenderer.java @@ -9,6 +9,7 @@ import net.minecraft.inventory.Slot; import org.lwjgl.opengl.GL11; +import codechicken.nei.NEIClientUtils; import codechicken.nei.PositionedStack; import codechicken.nei.guihook.GuiContainerManager; @@ -38,11 +39,7 @@ public class DefaultOverlayRenderer implements IRecipeOverlayRenderer { } private static void drawHover(int x, int y) { - GL11.glPushAttrib(GL11.GL_ALL_ATTRIB_BITS); - GL11.glDisable(GL11.GL_LIGHTING); - GL11.glDisable(GL11.GL_DEPTH_TEST); - drawRect(x, y, 16, 16, 0x66555555); - GL11.glPopAttrib(); + NEIClientUtils.gl2DRenderContext(() -> drawRect(x, y, 16, 16, 0x66555555)); } final IStackPositioner positioner; diff --git a/src/main/java/codechicken/nei/api/IBookmarkContainerHandler.java b/src/main/java/codechicken/nei/api/IBookmarkContainerHandler.java index a08eb6c7..60aa6dce 100644 --- a/src/main/java/codechicken/nei/api/IBookmarkContainerHandler.java +++ b/src/main/java/codechicken/nei/api/IBookmarkContainerHandler.java @@ -1,11 +1,26 @@ package codechicken.nei.api; import java.util.ArrayList; +import java.util.List; import net.minecraft.client.gui.inventory.GuiContainer; +import net.minecraft.inventory.Slot; import net.minecraft.item.ItemStack; public interface IBookmarkContainerHandler { + default List getStorageStacks(GuiContainer guiContainer) { + final List slots = guiContainer.inventorySlots.inventorySlots; + final List stacks = new ArrayList<>(); + + for (int i = 0; i < slots.size() - 4 * 9; i++) { + if (slots.get(i).getHasStack()) { + stacks.add(slots.get(i).getStack()); + } + } + + return stacks; + } + void pullBookmarkItemsFromContainer(GuiContainer guiContainer, ArrayList realItems); } diff --git a/src/main/java/codechicken/nei/api/IOverlayHandler.java b/src/main/java/codechicken/nei/api/IOverlayHandler.java index 187e42b7..a0e77630 100644 --- a/src/main/java/codechicken/nei/api/IOverlayHandler.java +++ b/src/main/java/codechicken/nei/api/IOverlayHandler.java @@ -14,7 +14,23 @@ import codechicken.nei.recipe.IRecipeHandler; public interface IOverlayHandler { - void overlayRecipe(GuiContainer firstGui, IRecipeHandler recipe, int recipeIndex, boolean shift); + void overlayRecipe(GuiContainer firstGui, IRecipeHandler recipe, int recipeIndex, boolean maxTransfer); + + default int transferRecipe(GuiContainer firstGui, IRecipeHandler recipe, int recipeIndex, int multiplier) { + return 0; + } + + default boolean canFillCraftingGrid(GuiContainer firstGui, IRecipeHandler recipe, int recipeIndex) { + return true; + } + + default boolean craft(GuiContainer firstGui, IRecipeHandler recipe, int recipeIndex, int multiplier) { + return false; + } + + default boolean canCraft(GuiContainer firstGui, IRecipeHandler handler, int recipeIndex) { + return false; + } default List presenceOverlay(GuiContainer firstGui, IRecipeHandler recipe, int recipeIndex) { final List itemPresenceSlots = new ArrayList<>(); @@ -25,7 +41,7 @@ public interface IOverlayHandler { && s.getStack().stackSize > 0 && s.isItemValid(s.getStack()) && s.canTakeStack(firstGui.mc.thePlayer)) - .map(s -> s.getStack().copy()).collect(Collectors.toCollection(ArrayList::new)); + .map(s -> s.getStack().copy()).collect(Collectors.toList()); for (PositionedStack stack : ingredients) { Optional used = invStacks.stream().filter(is -> is.stackSize > 0 && stack.contains(is)) diff --git a/src/main/java/codechicken/nei/api/ShortcutInputHandler.java b/src/main/java/codechicken/nei/api/ShortcutInputHandler.java index 93df9453..c5a810f5 100644 --- a/src/main/java/codechicken/nei/api/ShortcutInputHandler.java +++ b/src/main/java/codechicken/nei/api/ShortcutInputHandler.java @@ -1,30 +1,41 @@ package codechicken.nei.api; -import static codechicken.lib.gui.GuiDraw.getMousePosition; - import java.awt.Point; -import java.awt.Toolkit; -import java.awt.datatransfer.StringSelection; +import java.util.Arrays; +import java.util.Collections; import java.util.HashMap; import java.util.List; import java.util.Map; +import net.minecraft.client.gui.GuiScreen; import net.minecraft.client.gui.inventory.GuiContainer; import net.minecraft.item.ItemStack; import net.minecraftforge.oredict.OreDictionary; import org.lwjgl.input.Mouse; -import codechicken.nei.ItemPanel.ItemPanelSlot; +import codechicken.lib.gui.GuiDraw; +import codechicken.nei.BookmarkContainerInfo; +import codechicken.nei.FavoriteRecipes; import codechicken.nei.ItemPanels; +import codechicken.nei.ItemQuantityField; import codechicken.nei.LayoutManager; import codechicken.nei.NEIClientConfig; import codechicken.nei.NEIClientUtils; -import codechicken.nei.PositionedStack; -import codechicken.nei.recipe.BookmarkRecipeId; +import codechicken.nei.SearchField; +import codechicken.nei.bookmark.BookmarkGrid; +import codechicken.nei.bookmark.BookmarkItem; +import codechicken.nei.bookmark.BookmarksGridSlot; +import codechicken.nei.recipe.AutoCraftingManager; import codechicken.nei.recipe.GuiCraftingRecipe; import codechicken.nei.recipe.GuiRecipe; import codechicken.nei.recipe.GuiUsageRecipe; +import codechicken.nei.recipe.Recipe; +import codechicken.nei.recipe.Recipe.RecipeId; +import codechicken.nei.recipe.RecipeHandlerRef; +import codechicken.nei.recipe.StackInfo; +import codechicken.nei.recipe.chain.RecipeChainMath; +import codechicken.nei.util.NEIMouseUtils; public abstract class ShortcutInputHandler { @@ -34,35 +45,42 @@ public abstract class ShortcutInputHandler { return false; } - if (NEIClientConfig.isKeyHashDown("gui.overlay_hide")) { - return hideOverlayRecipe(); - } - - if (NEIClientConfig.isKeyHashDown("gui.bookmark_pull_items")) { - return ItemPanels.bookmarkPanel.pullBookmarkItems(ItemPanels.bookmarkPanel.getHoveredGroupId(true), false); - } - - if (NEIClientConfig.isKeyHashDown("gui.bookmark_pull_items_ingredients")) { - return ItemPanels.bookmarkPanel.pullBookmarkItems(ItemPanels.bookmarkPanel.getHoveredGroupId(true), true); - } - - if (stackover == null && NEIClientConfig.isKeyHashDown("gui.remove_recipe")) { - final int groupId = ItemPanels.bookmarkPanel.getHoveredGroupId(true); - - if (groupId != -1) { - ItemPanels.bookmarkPanel.removeGroup(groupId); - return true; - } + if (NEIClientConfig.isKeyHashDown("gui.overlay") && stackover == null + && LayoutManager.overlayRenderer != null) { + LayoutManager.overlayRenderer = null; + return true; } if (stackover == null) { + final int groupId = ItemPanels.bookmarkPanel.getHoveredGroupId(true); + + if (groupId != -1) { + + if (NEIClientConfig.isKeyHashDown("gui.remove_recipe")) { + ItemPanels.bookmarkPanel.removeGroup(groupId); + return true; + } + + if (NEIClientConfig.isKeyHashDown("gui.bookmark_pull_items")) { + return ItemPanels.bookmarkPanel.pullBookmarkItems(groupId, NEIClientUtils.shiftKey()); + } + + if (NEIClientConfig.autocraftingEnabled() && ItemPanels.bookmarkPanel.getGrid().isCraftingMode(groupId) + && NEIClientConfig.isKeyHashDown("gui.craft")) { + AutoCraftingManager + .runProcessing(ItemPanels.bookmarkPanel.getGrid().createRecipeChainMath(groupId)); + return true; + } + + } + return false; } stackover = stackover.copy(); if (NEIClientConfig.isKeyHashDown("gui.overlay")) { - return openOverlayRecipe(stackover, false); + return openOverlayRecipe(stackover); } if (NEIClientConfig.isKeyHashDown("gui.copy_name")) { @@ -73,10 +91,6 @@ public abstract class ShortcutInputHandler { return copyItemStackOreDictionary(stackover); } - if (NEIClientConfig.isKeyHashDown("gui.overlay_use")) { - return openOverlayRecipe(stackover, true); - } - if (NEIClientConfig.isKeyHashDown("gui.recipe")) { return GuiCraftingRecipe.openRecipeGui("item", stackover); } @@ -86,19 +100,15 @@ public abstract class ShortcutInputHandler { } if (NEIClientConfig.isKeyHashDown("gui.bookmark")) { - return saveRecipeInBookmark(stackover, false, false); + return saveRecipeInBookmark(stackover, NEIClientUtils.shiftKey(), NEIClientUtils.controlKey()); } - if (NEIClientConfig.isKeyHashDown("gui.bookmark_recipe")) { - return saveRecipeInBookmark(stackover, true, false); + if (NEIClientConfig.isKeyHashDown("gui.bookmark_pull_items")) { + return pullRecipeItems(stackover, NEIClientUtils.shiftKey()); } - if (NEIClientConfig.isKeyHashDown("gui.bookmark_count")) { - return saveRecipeInBookmark(stackover, false, true); - } - - if (NEIClientConfig.isKeyHashDown("gui.bookmark_recipe_count")) { - return saveRecipeInBookmark(stackover, true, true); + if (NEIClientConfig.autocraftingEnabled() && NEIClientConfig.isKeyHashDown("gui.craft")) { + return runAutoCrafting(stackover); } return false; @@ -123,19 +133,8 @@ public abstract class ShortcutInputHandler { return false; } - private static boolean hideOverlayRecipe() { - - if (LayoutManager.overlayRenderer != null) { - LayoutManager.overlayRenderer = null; - return true; - } - - return false; - } - private static boolean copyItemStackName(ItemStack stackover) { - Toolkit.getDefaultToolkit().getSystemClipboard() - .setContents(new StringSelection(stackover.getDisplayName()), null); + GuiScreen.setClipboardString(SearchField.getEscapedSearchText(stackover)); return true; } @@ -153,56 +152,310 @@ public abstract class ShortcutInputHandler { builder.deleteCharAt(builder.length() - 1); } - Toolkit.getDefaultToolkit().getSystemClipboard().setContents(new StringSelection(builder.toString()), null); + GuiScreen.setClipboardString(builder.toString()); return true; } - private static boolean openOverlayRecipe(ItemStack stackover, boolean shift) { - final GuiContainer gui = NEIClientUtils.getGuiContainer(); - - if (gui == null || gui instanceof GuiRecipe) { - return false; - } - - final Point mouseover = getMousePosition(); - ItemPanelSlot panelSlot = ItemPanels.bookmarkPanel.getSlotMouseOver(mouseover.x, mouseover.y); - BookmarkRecipeId recipeId = null; - - if (panelSlot != null) { - recipeId = ItemPanels.bookmarkPanel.getBookmarkRecipeId(panelSlot.slotIndex); - } else { - recipeId = ItemPanels.bookmarkPanel.getBookmarkRecipeId(stackover); - } + private static boolean runAutoCrafting(ItemStack stackover) { + final RecipeId recipeId = GuiCraftingRecipe.getRecipeId(NEIClientUtils.getGuiContainer(), stackover); if (recipeId != null) { - return GuiCraftingRecipe.overlayRecipe(stackover, recipeId, shift); + final Point mouseover = GuiDraw.getMousePosition(); + final BookmarksGridSlot slot = ItemPanels.bookmarkPanel.getSlotMouseOver(mouseover.x, mouseover.y); + RecipeChainMath math = null; + + if (slot == null) { + final Recipe recipe = Recipe.of(recipeId); + if (recipe != null) { + math = RecipeChainMath.of(recipe, 1); + } + } else if (slot.getRecipeId() != null && !slot.isIngredient()) { + + if (slot.getGroup().crafting == null) { + final Recipe recipe = Recipe.of(slot.getRecipeId()); + if (recipe != null) { + math = RecipeChainMath.of(recipe, slot != null ? slot.getMultiplier() : 1); + } + } else { + math = ItemPanels.bookmarkPanel.getGrid() + .createRecipeChainMath(slot.getGroupId(), slot.getRecipeId()); + } + + } + + if (math != null) { + AutoCraftingManager.runProcessing(math); + return true; + } } return false; } - private static boolean saveRecipeInBookmark(ItemStack stack, boolean saveIngredients, boolean saveStackSize) { + private static boolean openOverlayRecipe(ItemStack stackover) { + final GuiContainer gui = NEIClientUtils.getGuiContainer(); + final RecipeId recipeId = GuiCraftingRecipe.getRecipeId(gui, stackover); - if (stack != null) { - final GuiContainer gui = NEIClientUtils.getGuiContainer(); - List ingredients = null; - String handlerName = ""; + if (recipeId != null) { + final RecipeHandlerRef handlerRef = RecipeHandlerRef.of(recipeId); - if (gui instanceof GuiRecipe) { - ingredients = ((GuiRecipe) gui).getFocusedRecipeIngredients(); - handlerName = ((GuiRecipe) gui).getHandlerName(); - stack.stackSize = ((GuiRecipe) gui).prepareFocusedRecipeResultStackSize(stack); + if (handlerRef != null) { + + if (NEIClientUtils.shiftKey() && handlerRef.canFillCraftingGrid(gui)) { + int multiplier = 0; + + if (NEIClientUtils.controlKey()) { + final Point mouseover = GuiDraw.getMousePosition(); + final BookmarksGridSlot slot = ItemPanels.bookmarkPanel + .getSlotMouseOver(mouseover.x, mouseover.y); + multiplier = slot != null ? (int) Math.min(64, slot.getMultiplier()) : 1; + } + + handlerRef.fillCraftingGrid(gui, multiplier); + } else if (!NEIClientUtils.shiftKey() && handlerRef.canUseOverlayRenderer(gui)) { + handlerRef.useOverlayRenderer(gui); + } } - - ItemPanels.bookmarkPanel.addOrRemoveItem(stack, handlerName, ingredients, saveIngredients, saveStackSize); - return true; } return false; } + private static boolean saveRecipeInBookmark(ItemStack stackover, boolean saveIngredients, boolean saveStackSize) { + final Point mousePos = GuiDraw.getMousePosition(); + + if (!ItemPanels.bookmarkPanel.removeSlot(mousePos.x, mousePos.y, saveIngredients)) { + final Recipe recipe = getFocusedRecipe(stackover, mousePos.x, mousePos.y, saveIngredients); + + if (recipe != null && saveIngredients) { + + if (!ItemPanels.bookmarkPanel.removeRecipe(recipe.getRecipeId(), BookmarkGrid.DEFAULT_GROUP_ID)) { + final Recipe singleRecipe = Recipe.of( + Arrays.asList(recipe.getResult(stackover)), + recipe.getHandlerName(), + recipe.getIngredients()); + singleRecipe.setCustomRecipeId(recipe.getRecipeId()); + + ItemPanels.bookmarkPanel.addRecipe(singleRecipe, BookmarkGrid.DEFAULT_GROUP_ID); + } + + } else { + final RecipeId recipeId = recipe != null ? recipe.getRecipeId() : null; + final boolean existsRecipe = recipeId != null + && ItemPanels.bookmarkPanel.existsRecipe(recipeId, BookmarkGrid.DEFAULT_GROUP_ID); + + if (!ItemPanels.bookmarkPanel + .removeItem(stackover, existsRecipe ? recipeId : null, BookmarkGrid.DEFAULT_GROUP_ID)) { + saveStackSize = saveStackSize || saveIngredients + || ItemPanels.bookmarkPanel.existsRecipe(recipeId, BookmarkGrid.DEFAULT_GROUP_ID); + + if (!saveStackSize) { + stackover = StackInfo.withAmount(stackover, 0); + } else if (ItemPanels.itemPanel.contains(mousePos.x, mousePos.y) + || ItemPanels.itemPanel.historyPanel.contains(mousePos.x, mousePos.y)) { + stackover = ItemQuantityField.prepareStackWithQuantity(stackover, 0); + } + + ItemPanels.bookmarkPanel + .addItem(stackover, existsRecipe ? recipeId : null, BookmarkGrid.DEFAULT_GROUP_ID); + } + + } + } + + return true; + } + + private static Recipe getFocusedRecipe(ItemStack stackover, int mousex, int mousey, boolean useFavorites) { + final GuiContainer gui = NEIClientUtils.getGuiContainer(); + Recipe recipe = null; + + if (useFavorites && (ItemPanels.itemPanel.contains(mousex, mousey) + || ItemPanels.itemPanel.historyPanel.contains(mousex, mousey))) { + recipe = Recipe.of(FavoriteRecipes.getFavorite(stackover)); + + if (recipe != null) { + + recipe.getIngredients().stream().forEach(ingr -> { + final List permutations = ingr.getPermutations(); + for (int index = 0; index < permutations.size(); index++) { + if (FavoriteRecipes.getFavorite(permutations.get(index)) != null) { + ingr.setActiveIndex(index); + break; + } + } + }); + + } + + } else if (gui instanceof GuiRecipe guiRecipe) { + recipe = guiRecipe.getFocusedRecipe(); + } + + return recipe; + } + + private static boolean pullRecipeItems(ItemStack stackover, boolean shift) { + final Point mousePos = GuiDraw.getMousePosition(); + + if (ItemPanels.bookmarkPanel.contains(mousePos.x, mousePos.y)) { + stackover = ItemPanels.bookmarkPanel.getStackMouseOverWithQuantity(mousePos.x, mousePos.y); + } else if (ItemPanels.itemPanel.contains(mousePos.x, mousePos.y)) { + stackover = ItemPanels.itemPanel.getStackMouseOverWithQuantity(mousePos.x, mousePos.y); + } else if (ItemPanels.itemPanel.historyPanel.contains(mousePos.x, mousePos.y)) { + stackover = ItemPanels.itemPanel.historyPanel.getStackMouseOverWithQuantity(mousePos.x, mousePos.y); + } else { + stackover = ItemQuantityField.prepareStackWithQuantity(stackover, 0); + } + + final RecipeChainMath math = RecipeChainMath + .of(Arrays.asList(BookmarkItem.of(-3, stackover)), Collections.emptySet()); + return ItemPanels.bookmarkPanel.pullBookmarkItems(math, shift); + } + public static Map handleHotkeys(GuiContainer gui, int mousex, int mousey, ItemStack stack) { - return new HashMap<>(); + final int groupId = ItemPanels.bookmarkPanel.getHoveredGroupId(true); + final Map hotkeys = new HashMap<>(); + + if (groupId != -1) { + + hotkeys.put( + NEIClientConfig.getKeyName("gui.remove_recipe"), + NEIClientUtils.translate("bookmark.group.remove_recipe")); + + if (BookmarkContainerInfo.getBookmarkContainerHandler(gui) != null) { + hotkeys.put( + NEIClientConfig.getKeyName("gui.bookmark_pull_items"), + NEIClientUtils.translate("bookmark.group.pull_items")); + hotkeys.put( + NEIClientConfig.getKeyName("gui.bookmark_pull_items", NEIClientUtils.SHIFT_HASH), + NEIClientUtils.translate("bookmark.group.pull_items_shift")); + } + + if (NEIClientConfig.autocraftingEnabled() && ItemPanels.bookmarkPanel.getGrid().isCraftingMode(groupId)) { + hotkeys.put(NEIClientConfig.getKeyName("gui.craft"), NEIClientUtils.translate("bookmark.group.craft")); + } + } + + if (stack == null) { + return hotkeys; + } + + final BookmarksGridSlot slot = ItemPanels.bookmarkPanel.getSlotMouseOver(mousex, mousey); + final RecipeId recipeId = getHotkeyRecipeId(gui, mousex, mousey, stack, slot); + + if (slot != null) { + hotkeys.put( + NEIClientConfig.getKeyName("gui.bookmark"), + NEIClientUtils.translate(slot.isIngredient() ? "bookmark.remove_item" : "bookmark.remove_recipe")); + + if (slot.isIngredient()) { + hotkeys.put( + NEIClientConfig.getKeyName("gui.bookmark", NEIClientUtils.SHIFT_HASH), + NEIClientUtils.translate("bookmark.remove_recipe")); + } + + if (BookmarkContainerInfo.getBookmarkContainerHandler(gui) != null) { + hotkeys.put( + NEIClientConfig.getKeyName("gui.bookmark_pull_items"), + NEIClientUtils.translate("bookmark.group.pull_items")); + hotkeys.put( + NEIClientConfig.getKeyName("gui.bookmark_pull_items", NEIClientUtils.SHIFT_HASH), + NEIClientUtils.translate("bookmark.group.pull_items_shift")); + } + + } else { + hotkeys.put(NEIClientConfig.getKeyName("gui.bookmark"), NEIClientUtils.translate("bookmark.add_item")); + + if (StackInfo.getAmount(stack) > 0) { + hotkeys.put( + NEIClientConfig.getKeyName("gui.bookmark", NEIClientUtils.CTRL_HASH), + NEIClientUtils.translate("bookmark.add_item_with_count")); + } + + if (recipeId != null) { + hotkeys.put( + NEIClientConfig.getKeyName("gui.bookmark", NEIClientUtils.SHIFT_HASH), + NEIClientUtils.translate("bookmark.add_item_with_recipe")); + } + + } + + hotkeys.put(NEIClientConfig.getKeyName("gui.recipe"), NEIClientUtils.translate("itempanel.open_crafting")); + hotkeys.put(NEIClientConfig.getKeyName("gui.usage"), NEIClientUtils.translate("itempanel.open_usage")); + + hotkeys.put(NEIClientConfig.getKeyName("gui.copy_name"), NEIClientUtils.translate("itempanel.copy_name")); + hotkeys.put(NEIClientConfig.getKeyName("gui.copy_oredict"), NEIClientUtils.translate("itempanel.copy_oredict")); + + if (!(gui instanceof GuiRecipe) && NEIClientConfig.canCheatItem(stack)) { + hotkeys.put( + NEIClientUtils.getKeyName(NEIClientUtils.SHIFT_HASH, NEIMouseUtils.MOUSE_BTN_LMB), + NEIClientUtils.translate("itempanel.open_crafting")); + hotkeys.put( + NEIClientUtils.getKeyName(NEIClientUtils.SHIFT_HASH, NEIMouseUtils.MOUSE_BTN_RMB), + NEIClientUtils.translate("itempanel.open_usage")); + } else { + hotkeys.put( + NEIMouseUtils.getKeyName(NEIMouseUtils.MOUSE_BTN_LMB), + NEIClientUtils.translate("itempanel.open_crafting")); + hotkeys.put( + NEIMouseUtils.getKeyName(NEIMouseUtils.MOUSE_BTN_RMB), + NEIClientUtils.translate("itempanel.open_usage")); + } + + if (recipeId != null) { + final RecipeHandlerRef handlerRef = RecipeHandlerRef.of(recipeId); + + if (handlerRef != null) { + + if (handlerRef.canUseOverlayRenderer(gui)) { + hotkeys.put( + NEIClientConfig.getKeyName("gui.overlay"), + NEIClientUtils.translate("itempanel.overlay_recipe")); + } + + if (handlerRef.canFillCraftingGrid(gui)) { + hotkeys.put( + NEIClientConfig.getKeyName("gui.overlay", NEIClientUtils.SHIFT_HASH), + NEIClientUtils.translate("itempanel.fill_crafting_grid")); + + if (handlerRef.allowedTransferAlghoritm(gui)) { + hotkeys.put( + NEIClientConfig.getKeyName( + "gui.overlay", + NEIClientUtils.SHIFT_HASH + NEIClientUtils.CTRL_HASH), + NEIClientUtils.translate("itempanel.fill_crafting_grid_quantity")); + } + } + + if (NEIClientConfig.autocraftingEnabled()) { + hotkeys.put(NEIClientConfig.getKeyName("gui.craft"), NEIClientUtils.translate("itempanel.craft")); + } + } + + } + + return hotkeys; + } + + private static RecipeId getHotkeyRecipeId(GuiContainer gui, int mousex, int mousey, ItemStack stack, + BookmarksGridSlot slot) { + + if (slot != null) { + return slot.getRecipeId(); + } else if (ItemPanels.itemPanel.contains(mousex, mousey) + || ItemPanels.itemPanel.historyPanel.contains(mousex, mousey)) { + return FavoriteRecipes.getFavorite(stack); + } else + if (gui instanceof GuiRecipe guiRecipe) { + final Recipe focusedRecipe = guiRecipe.getFocusedRecipe(); + + if (focusedRecipe != null) { + return focusedRecipe.getRecipeId(); + } + } + + return null; } } diff --git a/src/main/java/codechicken/nei/bookmark/BookmarkGrid.java b/src/main/java/codechicken/nei/bookmark/BookmarkGrid.java new file mode 100644 index 00000000..53384f40 --- /dev/null +++ b/src/main/java/codechicken/nei/bookmark/BookmarkGrid.java @@ -0,0 +1,1291 @@ +package codechicken.nei.bookmark; + +import java.awt.Color; +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.LinkedHashSet; +import java.util.List; +import java.util.Map; +import java.util.Map.Entry; +import java.util.Objects; +import java.util.Set; +import java.util.WeakHashMap; +import java.util.stream.Collectors; + +import net.minecraft.item.ItemStack; + +import org.lwjgl.opengl.GL11; + +import codechicken.lib.gui.GuiDraw; +import codechicken.lib.vec.Rectangle4i; +import codechicken.nei.BookmarkPanel.BookmarkViewMode; +import codechicken.nei.ItemPanels; +import codechicken.nei.ItemStackAmount; +import codechicken.nei.ItemsGrid; +import codechicken.nei.ItemsGrid.MouseContext; +import codechicken.nei.LayoutManager; +import codechicken.nei.NEIClientConfig; +import codechicken.nei.NEIClientUtils; +import codechicken.nei.recipe.Recipe; +import codechicken.nei.recipe.Recipe.RecipeId; +import codechicken.nei.recipe.StackInfo; +import codechicken.nei.recipe.chain.RecipeChainMath; + +public class BookmarkGrid extends ItemsGrid { + + public static class BookmarkMouseContext extends MouseContext { + + public final int groupId; + public final RecipeId recipeId; + public final boolean controlKey; + public final boolean shiftKey; + + public BookmarkMouseContext(int slotIndex, int rowIndex, int columnIndex) { + super(slotIndex, rowIndex, columnIndex); + this.controlKey = NEIClientUtils.controlKey(); + this.shiftKey = NEIClientUtils.shiftKey(); + this.groupId = -1; + this.recipeId = null; + } + + public BookmarkMouseContext(int slotIndex, int rowIndex, int columnIndex, int groupId, RecipeId recipeId) { + super(slotIndex, rowIndex, columnIndex); + this.controlKey = NEIClientUtils.controlKey(); + this.shiftKey = NEIClientUtils.shiftKey(); + this.groupId = groupId; + this.recipeId = recipeId; + } + + @Override + public int hashCode() { + return Objects + .hash(this.groupId, this.rowIndex, this.slotIndex, this.recipeId, this.controlKey, this.shiftKey); + } + + public boolean equals(Object obj) { + + if (super.equals(obj) && obj instanceof BookmarkMouseContext hover) { + return this.groupId == hover.groupId && this.controlKey == hover.controlKey + && this.shiftKey == hover.shiftKey + && (this.recipeId == hover.recipeId + || this.recipeId != null && this.recipeId.equals(hover.recipeId)); + } + + return false; + } + + } + + protected static final Color DRAG_COLOR = new Color(0x66555555, true); + + public static final Color GROUP_CHAIN_COLOR = new Color(0x6645DA75, true); + public static final Color GROUP_NONE_COLOR = new Color(0xff666666, true); + public static final Color RECIPE_COLOR = new Color(0x99A033A0, true); + public static final Color PLACEHOLDER_COLOR = new Color(0x66222222, true); + + public static final int DEFAULT_GROUP_ID = 0; + protected static final int GROUP_PANEL_WIDTH = 7; + protected static final float SCALE_SPEED = 0.1f; + + protected List bookmarkItems = new ArrayList<>(); + protected final WeakHashMap animation = new WeakHashMap<>(); + + protected final Map groups = new HashMap<>(); + protected final BookmarkGridGenerator gridGenerator; + + private boolean isDirty = false; + + public BookmarkGrid() { + this.groups.put(DEFAULT_GROUP_ID, new BookmarkGroup(BookmarkViewMode.DEFAULT)); + this.gridGenerator = new BookmarkGridGenerator(this); + } + + public BookmarkViewMode getViewMode(int groupId) { + return this.groups.get(groupId).viewMode; + } + + public void setViewMode(int groupId, BookmarkViewMode mode) { + if (this.groups.get(groupId).viewMode != mode) { + this.groups.get(groupId).viewMode = mode; + onItemsChanged(); + } + } + + public void toggleViewMode(int groupId) { + this.groups.get(groupId).toggleViewMode(); + onItemsChanged(); + } + + public void toggleCollapsedState(int groupId) { + this.groups.get(groupId).toggleCollapsedState(); + onItemsChanged(); + } + + public void toggleCollapsedRecipe(int groupId, RecipeId recipeId) { + final BookmarkGroup group = getGroup(groupId); + final RecipeId collapsedRecipeId = group.crafting.recipeRelations.entrySet().stream() + .filter(entry -> entry.getValue().contains(recipeId)).map(entry -> entry.getKey()).findAny() + .orElse(recipeId); + + group.toggleCollapsedRecipe(collapsedRecipeId); + + if (group.crafting.recipeInMiddle.contains(collapsedRecipeId)) { + + if (group.collapsedRecipes.contains(collapsedRecipeId)) { + + for (BookmarkItem item : this.bookmarkItems) { + if (item.groupId == groupId && collapsedRecipeId.equals(item.recipeId) + && item.amount == item.factor) { + item.amount = 0; + } + } + + } else { + + for (BookmarkItem item : this.bookmarkItems) { + if (item.groupId == groupId && collapsedRecipeId.equals(item.recipeId) && item.amount == 0) { + item.amount = item.factor; + } + } + + } + + } + + onItemsChanged(); + } + + public boolean isCraftingMode(int groupId) { + return this.groups.get(groupId).crafting != null; + } + + public void setCraftingMode(int groupId, boolean on) { + this.groups.get(groupId).setCraftingMode(on); + onItemsChanged(); + } + + public void toggleCraftingMode(int groupId) { + this.groups.get(groupId).toggleCraftingMode(); + onItemsChanged(); + } + + public BookmarkGroup getGroup(int groupId) { + return this.groups.get(groupId); + } + + public int addGroup(BookmarkGroup group) { + int groupId = this.bookmarkItems.stream().mapToInt(item -> item.groupId).max().orElse(DEFAULT_GROUP_ID) + 1; + this.groups.put(groupId, group); + return groupId; + } + + @Override + public int getNumPages() { + + if (this.gridGenerator.gridMask == null) { + getMask(); + } + + return this.gridGenerator.pageCount; + } + + @Override + public int size() { + return this.bookmarkItems.size(); + } + + @Override + public boolean isEmpty() { + return this.bookmarkItems.isEmpty(); + } + + @Override + public List getMask() { + + if (this.isDirty) { + this.isDirty = false; + this.bookmarkItems = new ArrayList<>(new LinkedHashSet<>(this.bookmarkItems)); + sortIngredients(); + + this.realItems.clear(); + for (BookmarkItem item : this.bookmarkItems) { + this.realItems.add(item.itemStack); + } + } + + if (this.gridGenerator.gridMask != null) { + return this.gridGenerator.gridMask; + } + + this.gridGenerator.generate(); + this.page = Math.max(0, Math.min(this.page, this.gridGenerator.pageCount - 1)); + + if (ItemPanels.bookmarkPanel.sortableItem != null) { + ItemPanels.bookmarkPanel.sortableItem.update(); + } + + return this.gridGenerator.gridMask; + } + + @Override + protected BookmarkMouseContext getMouseContext(int mousex, int mousey) { + + if (contains(mousex, mousey) && !LayoutManager.bookmarkPanel.inEditingState()) { + return generateMouseContext(mousex, mousey); + } + + return null; + } + + private BookmarkMouseContext generateMouseContext(int mousex, int mousey) { + final BookmarksGridSlot hovered = getSlotMouseOver(mousex, mousey); + + if (hovered != null) { + return new BookmarkMouseContext( + hovered.slotIndex, + hovered.slotIndex / columns, + hovered.slotIndex % columns, + hovered.getGroupId(), + this.gridGenerator.itemToRecipe.getOrDefault(hovered.itemIndex, hovered.getRecipeId())); + } + + final int rowIndex = getHoveredRowIndex(false); + + if (rowIndex != -1) { + final int groupId = getRowGroupId(rowIndex); + final int rowItemIndex = getRowItemIndex(rowIndex, false); + + if (rowItemIndex != Integer.MIN_VALUE) { + int columnIndex = getColumnIndex(mousex); + BookmarkGroup group = getGroup(groupId); + RecipeId recipeId = null; + + if (!group.collapsed) { + recipeId = this.gridGenerator.itemToRecipe + .getOrDefault(rowItemIndex, getCalculatedItem(rowItemIndex).recipeId); + } + + return new BookmarkMouseContext( + -1, + rowIndex, + columnIndex < 0 || columnIndex > this.columns ? -1 : columnIndex, + groupId, + recipeId); + + } + + } + + return null; + } + + public int getRowGroupId(int rowIndex) { + + if (this.gridGenerator.gridMask == null) { + getMask(); + } + + return this.gridGenerator.getRowGroupId(rowIndex); + } + + public int getRowItemIndex(int rowIndex, boolean firstItemIndex) { + + if (this.gridGenerator.gridMask == null) { + getMask(); + } + + int absoluteSlotIndex = (this.page * this.rows + rowIndex) * this.columns + + (firstItemIndex ? 0 : this.columns - 1); + + for (int index = 0; index < this.columns; index++) { + if (this.gridGenerator.slotToItem.containsKey(absoluteSlotIndex + index * (firstItemIndex ? 1 : -1))) { + return this.gridGenerator.slotToItem.get(absoluteSlotIndex + index * (firstItemIndex ? 1 : -1)); + } + } + + return Integer.MIN_VALUE; + } + + public int getHoveredRowIndex(boolean groupPanel) { + final Point mouse = GuiDraw.getMousePosition(); + final int leftBorder = marginLeft + paddingLeft; + final int r = getRowIndex(mouse.y); + + if (this.isEmpty()) { + return -1; + } + + if (!(new Rectangle4i( + leftBorder - (groupPanel ? BookmarkGrid.GROUP_PANEL_WIDTH : 0), + marginTop, + columns * SLOT_SIZE, + (getLastRowIndex() + 1) * SLOT_SIZE)).contains(mouse.x, mouse.y)) { + return -1; + } + + if (groupPanel && mouse.x >= leftBorder - GROUP_PANEL_WIDTH && mouse.x < leftBorder) { + return r; + } + + if (!groupPanel && !isInvalidSlot(columns * r + ((mouse.x - leftBorder) / SLOT_SIZE))) { + return r; + } + + return -1; + } + + @Override + public void setGridSize(int mleft, int mtop, int w, int h) { + super.setGridSize(mleft + GROUP_PANEL_WIDTH, mtop, w - GROUP_PANEL_WIDTH, h); + } + + private void sortIngredients() { + + for (int itemIndex = 0; itemIndex < this.bookmarkItems.size(); itemIndex++) { + final BookmarkItem item = this.bookmarkItems.get(itemIndex); + + if (item.groupId != DEFAULT_GROUP_ID) { + final List items = this.bookmarkItems.stream().filter(itm -> itm.groupId == item.groupId) + .collect(Collectors.toList()); + this.bookmarkItems.removeAll(items); + this.bookmarkItems.addAll(itemIndex, items); + itemIndex += items.size() - 1; + } + } + + for (Entry entry : this.groups.entrySet()) { + final Map recipeState = new HashMap<>(); + final BookmarkGroup group = entry.getValue(); + final int groupId = entry.getKey(); + + if (group.viewMode == BookmarkViewMode.TODO_LIST || group.crafting != null) { + sortItemsInsideGroup(groupId); + } + + for (BookmarkItem item : this.bookmarkItems) { + if (item.groupId == groupId && item.recipeId != null) { + recipeState.put( + item.recipeId, + recipeState.getOrDefault(item.recipeId, 0) | (item.isIngredient ? 1 : 2)); + } + } + + for (BookmarkItem item : this.bookmarkItems) { + if (item.groupId == groupId && item.isIngredient + && (item.recipeId == null || recipeState.getOrDefault(item.recipeId, 1) == 1)) { + recipeState.remove(item.recipeId); + item.isIngredient = false; + item.recipeId = null; + } + } + + group.collapsedRecipes.removeIf(recipeId -> !recipeState.containsKey(recipeId)); + + if (group.crafting != null) { + group.crafting.refresh(createChainItems(groupId), group.collapsedRecipes); + } + } + + } + + public void sortItemsInsideGroup(int groupId) { + final int size = this.bookmarkItems.size(); + int itemIndex = 0; + + while (itemIndex < size) { + final BookmarkItem item = this.bookmarkItems.get(itemIndex); + if (item.groupId == groupId && item.recipeId != null) { + final Map sortingRank = new HashMap<>(); + final List sortedItems = new ArrayList<>(); + final List items = new ArrayList<>(); + + for (int index = itemIndex; index < size; index++) { + if (item.equalsRecipe(this.bookmarkItems.get(index))) { + sortingRank.put(index, this.bookmarkItems.get(index).isIngredient ? 1 : -1); + items.add(index); + } + } + + items.sort((a, b) -> sortingRank.get(a) - sortingRank.get(b)); + + for (int index : items) { + sortedItems.add(this.bookmarkItems.get(index)); + } + + this.bookmarkItems.removeAll(sortedItems); + this.bookmarkItems.addAll(itemIndex, sortedItems); + itemIndex += sortedItems.size(); + } else { + itemIndex++; + } + } + + } + + @Override + public void draw(int mousex, int mousey) { + if (getPerPage() == 0) { + return; + } + + if (this.gridGenerator.gridMask == null) { + getMask(); + } + + if (!this.isEmpty() && this.gridGenerator.maxAbsoluteSlotIndex > 0) { + final GroupingItem groupingItem = ItemPanels.bookmarkPanel.groupingItem; + final int sortableGroupId = ItemPanels.bookmarkPanel.sortableGroup != null + ? ItemPanels.bookmarkPanel.sortableGroup.groupId + : -3; + final int rowShift = this.page * this.rows; + Map groups = this.gridGenerator.rowToGroupId; + + if (groupingItem != null && groupingItem.hasEndRow()) { + groups = groupingItem.prepareGroups(groups); + } else if (sortableGroupId != -3) { + + for (int rowIndex = 0; rowIndex < this.rows; rowIndex++) { + if (groups.getOrDefault(rowShift + rowIndex, DEFAULT_GROUP_ID) == sortableGroupId) { + NEIClientUtils.drawRect( + marginLeft + paddingLeft - GROUP_PANEL_WIDTH, + marginTop + rowIndex * SLOT_SIZE, + GROUP_PANEL_WIDTH, + SLOT_SIZE, + DRAG_COLOR); + } + } + + } else if (!ItemPanels.bookmarkPanel.inEditingState()) { + final int focusedRowIndex = getHoveredRowIndex(true); + + if (focusedRowIndex != -1) { + NEIClientUtils.drawRect( + marginLeft + paddingLeft - GROUP_PANEL_WIDTH, + marginTop + focusedRowIndex * SLOT_SIZE, + GROUP_PANEL_WIDTH, + SLOT_SIZE, + HIGHLIGHT_COLOR); + } + } + + int previousPageGroupId = groups.getOrDefault(rowShift - 1, DEFAULT_GROUP_ID); + int nextPageGroupId = groups.getOrDefault(rowShift + this.rows, DEFAULT_GROUP_ID); + int previousGroupId = DEFAULT_GROUP_ID; + int groupStartIndex = -2; + + if (previousPageGroupId == groups.get(rowShift)) { + previousGroupId = previousPageGroupId; + groupStartIndex = -1; + } + + for (int rowIndex = 0; rowIndex < this.rows && groups.containsKey(rowShift + rowIndex); rowIndex++) { + int groupId = groups.get(rowShift + rowIndex); + + if (groupStartIndex != -2 && previousGroupId != groupId) { + + if (previousGroupId != DEFAULT_GROUP_ID && previousGroupId != sortableGroupId) { + drawGroup(previousGroupId, groupStartIndex, rowIndex - 1); + } else { + drawShadowGroup(groupStartIndex, rowIndex - 1); + } + + groupStartIndex = -2; + } + + if (groupStartIndex == -2) { + groupStartIndex = rowIndex; + } + + previousGroupId = groupId; + } + + if (groupStartIndex != -2) { + final int rowIndex = nextPageGroupId != DEFAULT_GROUP_ID + && nextPageGroupId == groups.get(rowShift + this.rows - 1) ? this.rows : getLastRowIndex(); + + if (previousGroupId != DEFAULT_GROUP_ID && previousGroupId != sortableGroupId) { + drawGroup(previousGroupId, groupStartIndex, rowIndex); + } else { + drawShadowGroup(groupStartIndex, rowIndex); + } + } + + } + + super.draw(mousex, mousey); + } + + public void drawGroup(int groupId, int rowIndexStart, int rowIndexEnd) { + final int halfWidth = GROUP_PANEL_WIDTH / 2; + final int heightPadding = SLOT_SIZE / 4; + final int leftPosition = marginLeft + paddingLeft - halfWidth - 1; + final Color color = groupId > DEFAULT_GROUP_ID && this.groups.get(groupId).crafting != null ? GROUP_CHAIN_COLOR + : GROUP_NONE_COLOR; + int height = (Math.min(rowIndexEnd, this.rows - 1) - Math.max(0, rowIndexStart) + 1) * SLOT_SIZE; + int top = marginTop + Math.max(0, rowIndexStart) * SLOT_SIZE; + + if (rowIndexStart >= 0) { + NEIClientUtils + .drawRect(leftPosition, marginTop + rowIndexStart * SLOT_SIZE + heightPadding, halfWidth, 1, color); + top += heightPadding + 1; + height -= heightPadding + 1; + } + + if (rowIndexEnd < this.rows) { + NEIClientUtils.drawRect( + leftPosition, + marginTop + (rowIndexEnd + 1) * SLOT_SIZE - heightPadding, + halfWidth, + 1, + color); + height -= heightPadding; + } + + NEIClientUtils.drawRect(leftPosition, top, 1, height, color); + } + + private void drawShadowGroup(int rowIndexStart, int rowIndexEnd) { + int top = marginTop + Math.max(0, rowIndexStart) * SLOT_SIZE; + int height = (Math.min(rowIndexEnd, this.rows - 1) - Math.max(0, rowIndexStart) + 1) * SLOT_SIZE; + + NEIClientUtils.drawRect( + marginLeft + paddingLeft - GROUP_PANEL_WIDTH / 2 - 1, + top + 2, + 1, + height - 4, + PLACEHOLDER_COLOR); + } + + @Override + public void onItemsChanged() { + this.isDirty = true; + onGridChanged(); + } + + @Override + public void onGridChanged() { + this.gridGenerator.gridMask = null; + super.onGridChanged(); + } + + public Map createChainItems(int groupId) { + final Map chainItems = new HashMap<>(); + + for (int itemIndex = 0; itemIndex < this.bookmarkItems.size(); itemIndex++) { + BookmarkItem item = this.bookmarkItems.get(itemIndex); + if (item.groupId == groupId) { + chainItems.put(itemIndex, item.copy()); + } + } + + return chainItems; + } + + public RecipeChainMath createRecipeChainMath(int groupId) { + return createRecipeChainMath(groupId, null); + } + + public RecipeChainMath createRecipeChainMath(int groupId, RecipeId recipeId) { + final BookmarkGroup group = getGroup(groupId); + final List chainItems = new ArrayList<>(); + final Set collapsedRecipes = group.crafting != null && recipeId == null ? group.collapsedRecipes + : Collections.emptySet(); + Set recipeRelations = null; + + if (group.collapsed) { + recipeId = null; + } + + if (group.crafting != null && recipeId != null) { + for (Map.Entry> entry : group.crafting.recipeRelations.entrySet()) { + if (entry.getValue().contains(recipeId)) { + recipeId = entry.getKey(); + break; + } + } + } + + if (group.crafting != null && group.crafting.recipeRelations.containsKey(recipeId)) { + recipeRelations = group.crafting.recipeRelations.get(recipeId); + } else if (recipeId != null) { + recipeRelations = new HashSet<>(Arrays.asList(recipeId)); + } + + for (int itemIndex = 0; itemIndex < this.bookmarkItems.size(); itemIndex++) { + final BookmarkItem item = this.bookmarkItems.get(itemIndex); + if (item.groupId == groupId && (recipeRelations == null || recipeRelations.contains(item.recipeId))) { + long amount = item.amount; + + if (recipeId != null && group.crafting != null + && group.crafting.calculatedItems.containsKey(itemIndex) + && recipeId.equals(item.recipeId)) { + amount = group.crafting.calculatedItems.get(itemIndex).getCalculatedAmount(); + } + + chainItems.add(item.copyWithAmount(amount)); + } + } + + if (group.crafting == null) { + for (BookmarkItem item : chainItems) { + item.recipeId = null; + } + } + + return RecipeChainMath.of(chainItems, collapsedRecipes); + } + + public void createGroup(GroupingItem groupingItem) { + final Map> rowItems = collectItemsToRows(); + final Map newGroups = new HashMap<>(); + boolean change = false; + + for (Map.Entry entry : groupingItem.prepareGroups(this.gridGenerator.rowToGroupId) + .entrySet()) { + if (this.gridGenerator.rowToGroupId.get(entry.getKey()) != entry.getValue()) { + int groupId = entry.getValue(); + + if (groupId == Integer.MIN_VALUE) { + groupId = newGroups.computeIfAbsent( + groupId, + grId -> addGroup(new BookmarkGroup(getViewMode(DEFAULT_GROUP_ID)))); + } else if (groupId < 0) { + groupId = newGroups + .computeIfAbsent(groupId, grId -> addGroup(new BookmarkGroup(getViewMode(Math.abs(grId))))); + } + + for (BookmarkItem item : rowItems.getOrDefault(entry.getKey(), Collections.emptyList())) { + item.groupId = groupId; + } + + change = true; + } + } + + if (change) { + this.bookmarkItems = rowItems.values().stream().flatMap(items -> items.stream()) + .collect(Collectors.toList()); + + final Set usedGroupIds = new HashSet<>(); + for (BookmarkItem item : this.bookmarkItems) { + usedGroupIds.add(item.groupId); + } + + this.groups.keySet().removeIf(k -> k != DEFAULT_GROUP_ID && !usedGroupIds.contains(k)); + + onItemsChanged(); + } + + } + + private Map> collectItemsToRows() { + final Map> results = new HashMap<>(); + final int totalRows = (int) Math.ceil(this.gridGenerator.maxAbsoluteSlotIndex / (float) this.columns); + final List allItems = new ArrayList<>(this.bookmarkItems); + + for (int rowIndex = 0; rowIndex < totalRows; rowIndex++) { + int itemIndex = Integer.MIN_VALUE; + + for (int index = 0; index < this.columns; index++) { + if (this.gridGenerator.slotToItem.containsKey(rowIndex * this.columns + index)) { + itemIndex = this.gridGenerator.slotToItem.get(rowIndex * this.columns + index); + break; + } + } + + if (itemIndex != Integer.MIN_VALUE) { + final BookmarkItem item = getCalculatedItem(itemIndex); + final BookmarkGroup group = getGroup(item.groupId); + final List items = new ArrayList<>(); + + if (group.collapsed || group.crafting != null && group.viewMode == BookmarkViewMode.DEFAULT) { + + for (BookmarkItem itm : allItems) { + if (itm.groupId == item.groupId) { + items.add(itm); + } + } + + } else if (item.recipeId != null && group.crafting != null + && group.crafting.itemToRecipe.containsKey(itemIndex)) { + final Set recipeRelations = group.crafting.recipeRelations + .getOrDefault(item.recipeId, Collections.singleton(item.recipeId)); + + for (BookmarkItem itm : allItems) { + if (itm.groupId == item.groupId && recipeRelations.contains(itm.recipeId)) { + items.add(itm); + } + } + + } else { + + for (int index = 0; index < this.columns; index++) { + if (this.gridGenerator.slotToItem.containsKey(rowIndex * this.columns + index)) { + final BookmarkItem itm = this.bookmarkItems + .get(this.gridGenerator.slotToItem.get(rowIndex * this.columns + index)); + if (allItems.contains(itm)) { + items.add(itm); + } + } + } + + } + + results.put(rowIndex, items); + allItems.removeAll(items); + } + + } + + results.put(totalRows, allItems); + + return results; + } + + public void removeGroup(int groupId) { + this.bookmarkItems.removeIf(item -> item.groupId == groupId); + + if (groupId != DEFAULT_GROUP_ID) { + this.groups.remove(groupId); + } + + onItemsChanged(); + } + + public void moveGroup(SortableGroup sortableGroup, int overRowIndex) { + int absoluteRowIndex = this.page * this.rows + overRowIndex; + int absoluteSlotIndex = absoluteRowIndex * this.columns; + int overGroupId = getRowGroupId(overRowIndex); + + if (sortableGroup.groupId == overGroupId) { + return; + } + + boolean mouseDown = false; + + for (int rowId = 0; rowId < absoluteRowIndex && !mouseDown; rowId++) { + if (this.gridGenerator.rowToGroupId.get(rowId) == sortableGroup.groupId) { + mouseDown = true; + } + } + + if (mouseDown) { + absoluteRowIndex += 1; + absoluteSlotIndex += this.columns; + } + + if (mouseDown && absoluteSlotIndex >= this.gridGenerator.maxAbsoluteSlotIndex) { + final int itemIndex = this.gridGenerator.slotToItem + .getOrDefault(this.gridGenerator.maxAbsoluteSlotIndex - 1, Integer.MIN_VALUE); + + if (itemIndex == Integer.MIN_VALUE || getCalculatedItem(itemIndex).groupId == sortableGroup.groupId) { + return; + } + + moveItems(null, sortableGroup.getBookmarkItems()); + } else if (this.gridGenerator.rowToGroupId.containsKey(absoluteRowIndex)) { + overGroupId = this.gridGenerator.rowToGroupId.get(absoluteRowIndex); + + if (overGroupId != DEFAULT_GROUP_ID && overGroupId + == this.gridGenerator.rowToGroupId.getOrDefault(absoluteRowIndex - 1, DEFAULT_GROUP_ID)) { + return; + } + + final int itemIndex = this.gridGenerator.slotToItem.getOrDefault(absoluteSlotIndex, Integer.MIN_VALUE); + BookmarkItem bookmarkItem = getCalculatedItem(itemIndex); + + if (bookmarkItem == null) { + return; + } + + if (bookmarkItem.groupId != DEFAULT_GROUP_ID) { + for (int i = 0; i < this.bookmarkItems.size(); i++) { + if (this.bookmarkItems.get(i).groupId == bookmarkItem.groupId) { + bookmarkItem = this.bookmarkItems.get(i); + break; + } + } + } + + moveItems(bookmarkItem, sortableGroup.getBookmarkItems()); + } + + } + + public int indexOf(int groupId, ItemStack stackA, RecipeId recipeId, boolean ingredient) { + + for (int itemIndex = 0; itemIndex < this.bookmarkItems.size(); itemIndex++) { + final BookmarkItem item = this.bookmarkItems.get(itemIndex); + if (item.groupId == groupId && item.isIngredient == ingredient + && (recipeId == item.recipeId || recipeId != null && recipeId.equals(item.recipeId)) + && StackInfo.equalItemAndNBT(stackA, item.itemStack, true)) { + return itemIndex; + } + } + + return -1; + } + + public BookmarkItem getCalculatedItem(int itemIndex) { + + if (this.gridGenerator.caclulatedItems.containsKey(itemIndex)) { + return this.gridGenerator.caclulatedItems.get(itemIndex).getItem(); + } + + return null; + } + + public BookmarkItem getBookmarkItem(int itemIndex) { + + if (itemIndex < 0 || itemIndex >= this.bookmarkItems.size()) { + return null; + } + + return this.bookmarkItems.get(itemIndex); + } + + public int getAbsoluteSlotIndex(int itemIndex) { + return this.gridGenerator.itemToSlot.getOrDefault(itemIndex, -1); + } + + public List getSortedItems() { + return new ArrayList<>(this.gridGenerator.slotToItem.values()); + } + + public void addItem(BookmarkItem item, boolean animate) { + this.bookmarkItems.add(item); + + if (animate && NEIClientConfig.getGridRenderingCacheMode() == 0 && NEIClientConfig.areBookmarksAnimated()) { + this.animation.put(item, 0f); + } + + onItemsChanged(); + } + + public void addRecipe(Recipe recipe, int groupId) { + final RecipeId recipeId = recipe.getRecipeId(); + final List results = recipe.getResults().stream().map(res -> res.getItemStack()) + .collect(Collectors.toList()); + final List ingredients = recipe.getIngredients().stream().map(ingr -> ingr.getItemStack()) + .collect(Collectors.toList()); + + for (ItemStack stack : ItemStackAmount.of(results).values()) { + this.addItem(BookmarkItem.of(groupId, stack, StackInfo.getAmount(stack), recipeId, false), true); + } + + for (ItemStack stack : ItemStackAmount.of(ingredients).values()) { + this.addItem( + BookmarkItem.of( + groupId, + stack, + StackInfo.getAmount(stack), + recipeId, + true, + BookmarkItem.generatePermutations(stack, recipe)), + true); + } + } + + public void removeRecipe(int itemIndex, boolean removeFullRecipe) { + final BookmarkItem item = getCalculatedItem(itemIndex); + + if (item == null) { + return; + } + + if (!removeFullRecipe && item.recipeId != null && !item.isIngredient) { + removeFullRecipe = this.bookmarkItems.stream() + .noneMatch(m -> !m.isIngredient && !m.equals(item) && item.equalsRecipe(m)); + } + + if (item.recipeId != null && removeFullRecipe) { + removeRecipe(item.recipeId, item.groupId); + } else { + this.bookmarkItems.remove(itemIndex); + onItemsChanged(); + } + } + + public boolean removeRecipe(RecipeId recipeId, int groupId) { + + if (this.bookmarkItems.removeIf(item -> item.equalsRecipe(recipeId, groupId))) { + onItemsChanged(); + return true; + } + + return false; + } + + public boolean existsRecipe(RecipeId recipeId, int groupId) { + return this.bookmarkItems.stream().anyMatch(item -> item.equalsRecipe(recipeId, groupId)); + } + + public RecipeId getRecipeId(int itemIndex) { + final BookmarkItem bookmarkItem = getCalculatedItem(itemIndex); + return bookmarkItem != null ? bookmarkItem.recipeId : null; + } + + public void moveItem(SortableItem sortableItem, int overRowIndex, int overGroupId, boolean moveAfter) { + final BookmarkGroup overGroup = getGroup(overGroupId); + int absoluteSlotIndex = (this.page * this.rows + overRowIndex) * this.columns; + + if (sortableItem.isCollapsedRecipe && sortableItem.bookmarkItem.groupId != overGroupId || overGroup.collapsed + || overGroup.viewMode == BookmarkViewMode.DEFAULT) { + return; + } + + if (sortableItem.bookmarkItem.groupId != overGroupId) { + + for (BookmarkItem item : this.bookmarkItems) { + if (sortableItem.containsItem(item)) { + item.groupId = overGroupId; + } + } + + sortableItem.bookmarkItem.groupId = overGroupId; + onItemsChanged(); + } + + if (moveAfter) { + absoluteSlotIndex += this.columns; + } + + if (moveAfter && absoluteSlotIndex >= this.gridGenerator.maxAbsoluteSlotIndex) { + final int itemIndex = this.gridGenerator.slotToItem + .getOrDefault(this.gridGenerator.maxAbsoluteSlotIndex - 1, Integer.MIN_VALUE); + + if (itemIndex == Integer.MIN_VALUE || sortableItem.items.contains(itemIndex)) { + return; + } + + moveItems(null, sortableItem.getBookmarkItems()); + } else if (this.gridGenerator.slotToItem.containsKey(absoluteSlotIndex)) { + final int itemIndex = this.gridGenerator.slotToItem.get(absoluteSlotIndex); + final List sortedIndexes = new ArrayList<>(this.gridGenerator.slotToItem.values()); + BookmarkItem bookmarkItem = getCalculatedItem(itemIndex); + final BookmarkGroup group = getGroup(bookmarkItem.groupId); + final int sortIndex = sortedIndexes.indexOf(itemIndex); + + if (sortIndex == -1 || sortableItem.items.contains(itemIndex) + || sortIndex > 0 && sortableItem.items.contains(sortedIndexes.get(sortIndex - 1))) { + return; + } + + if (group.crafting != null && sortIndex > 0) { + final RecipeId recipeId = group.crafting.itemToRecipe.getOrDefault(itemIndex, bookmarkItem.recipeId); + + if (recipeId != null) { + if (recipeId.equals(this.gridGenerator.itemToRecipe.get(sortedIndexes.get(sortIndex - 1)))) { + return; + } + + for (int i = 0; i < this.bookmarkItems.size(); i++) { + if (this.bookmarkItems.get(i).groupId == bookmarkItem.groupId + && recipeId.equals(this.bookmarkItems.get(i).recipeId)) { + bookmarkItem = this.bookmarkItems.get(i); + break; + } + } + } + } + + moveItems(bookmarkItem, sortableItem.getBookmarkItems()); + } + + } + + public void moveItem(SortableItem sortableItem, int itemIndex) { + final List sortedIndexes = new ArrayList<>(this.gridGenerator.slotToItem.values()); + int sortIndex = sortedIndexes.indexOf(itemIndex); + final boolean moveAfter = sortIndex > sortedIndexes.indexOf(sortableItem.getItemIndex()); + final BookmarkGroup group = getGroup(sortableItem.bookmarkItem.groupId); + BookmarkItem bookmarkItem = getCalculatedItem(itemIndex); + + if (bookmarkItem == null || sortIndex == -1 || sortableItem.items.contains(itemIndex) || group.collapsed) { + return; + } + + if (moveAfter) { + sortIndex = findNextBookmarkItem(itemIndex); + + if (sortIndex == -1) { + return; + } + + if (sortIndex < sortedIndexes.size()) { + bookmarkItem = getCalculatedItem(sortedIndexes.get(sortIndex)); + } else { + bookmarkItem = null; + } + } + + if (bookmarkItem != null && (group.crafting != null && group.viewMode == BookmarkViewMode.DEFAULT + || group.viewMode == BookmarkViewMode.TODO_LIST)) { + + while (sortIndex > 0 && bookmarkItem.equalsRecipe(getCalculatedItem(sortedIndexes.get(sortIndex - 1)))) { + sortIndex--; + } + + bookmarkItem = getCalculatedItem(sortedIndexes.get(sortIndex)); + } + + moveItems(bookmarkItem, sortableItem.getBookmarkItems()); + } + + private int findNextBookmarkItem(int itemIndex) { + final List sortedIndexes = new ArrayList<>(this.gridGenerator.slotToItem.values()); + final int sortIndex = sortedIndexes.indexOf(itemIndex); + final BookmarkItem target = getCalculatedItem(itemIndex); + final BookmarkGroup group = getGroup(target.groupId); + int nextSortIndex = sortIndex + 1; + + if (group.viewMode == BookmarkViewMode.TODO_LIST) { + final int rowId = this.gridGenerator.itemToSlot.get(itemIndex) / this.columns; + + while (nextSortIndex < sortedIndexes.size() + && (this.gridGenerator.itemToSlot.get(sortedIndexes.get(nextSortIndex)) / this.columns) == rowId) { + nextSortIndex++; + } + } + + if (nextSortIndex < sortedIndexes.size()) { + final int nextItemIndex = sortedIndexes.get(nextSortIndex); + + if (group.viewMode == BookmarkViewMode.TODO_LIST && target.equalsRecipe(getCalculatedItem(nextItemIndex))) { + return -1; + } + + if (group.crafting != null) { + final RecipeId recipeId = group.crafting.itemToRecipe.get(itemIndex); + + if (recipeId != null && recipeId.equals(this.gridGenerator.itemToRecipe.get(nextItemIndex))) { + return -1; + } + } + + return nextSortIndex; + } + + return Integer.MAX_VALUE; + } + + private void moveItems(BookmarkItem bookmarkItem, List items) { + this.bookmarkItems.removeAll(items); + final int index = bookmarkItem == null ? this.bookmarkItems.size() : this.bookmarkItems.indexOf(bookmarkItem); + this.bookmarkItems.addAll(index, items); + onItemsChanged(); + } + + public void shiftGroupAmount(int targetGroupId, long shift) { + final BookmarkGroup group = getGroup(targetGroupId); + + if (group.crafting == null) { + final List items = new ArrayList<>(); + final Set sizes = new HashSet<>(); + + for (BookmarkItem item : this.bookmarkItems) { + if (item.groupId == targetGroupId && item.amount > 0) { + sizes.add(Math.max(item.getStackSize(), item.factor / item.fluidCellAmount)); + items.add(item); + } + } + + final long gcd = sizes.stream().reduce(0L, (a, b) -> gcd(a, b)); + long multiplier = Integer.MAX_VALUE; + + for (BookmarkItem item : items) { + long customFactor = (long) Math.ceil(item.amount / (double) gcd); + long customMultiplier = (long) (customFactor > 0 ? Math.ceil(item.amount / (double) customFactor) : 0); + multiplier = Math.min(shiftMultiplier(customMultiplier, shift, 1), multiplier); + } + + for (BookmarkItem item : items) { + long customFactor = (long) Math.ceil(item.amount / (double) gcd); + item.amount = customFactor * multiplier; + } + } else { + final Map recipeMultipliers = new HashMap<>(); + final Set outputRecipes = group.crafting.outputRecipes; + final List items = new ArrayList<>(); + + for (BookmarkItem item : this.bookmarkItems) { + if (item.factor > 0 + && outputRecipes.stream().anyMatch(recipeId -> item.equalsRecipe(recipeId, targetGroupId))) { + final long multiplier = recipeMultipliers.getOrDefault(item.recipeId, (long) Integer.MAX_VALUE); + final boolean isOutputRecipe = group.crafting != null + && !group.crafting.recipeInMiddle.contains(item.recipeId); + + recipeMultipliers.put( + item.recipeId, + Math.min( + shiftMultiplier( + Math.max(isOutputRecipe ? 1 : 0, item.getMultiplier()), + shift, + isOutputRecipe ? 1 : 0), + multiplier)); + items.add(item); + } + } + + for (BookmarkItem item : items) { + item.amount = item.factor * recipeMultipliers.getOrDefault(item.recipeId, 0L); + } + } + + this.onItemsChanged(); + } + + public void shiftItemAmount(int targetItemIndex, long shift) { + final BookmarkItem targetItem = getCalculatedItem(targetItemIndex); + final BookmarkGroup group = getGroup(targetItem.groupId); + + if (group.collapsed) { + shiftGroupAmount(targetItem.groupId, shift); + } else if (targetItem.recipeId != null) { + final RecipeId recipeId = this.gridGenerator.itemToRecipe + .getOrDefault(targetItemIndex, targetItem.recipeId); + final boolean isOutputRecipe = group.crafting != null && !group.crafting.recipeInMiddle.contains(recipeId); + long multiplier = Integer.MAX_VALUE; + + for (BookmarkItem item : this.bookmarkItems) { + if (item.equalsRecipe(recipeId, targetItem.groupId) && item.factor > 0) { + multiplier = Math.min( + shiftMultiplier( + Math.max(isOutputRecipe ? 1 : 0, item.getMultiplier()), + shift, + isOutputRecipe ? 1 : 0), + multiplier); + } + } + + for (BookmarkItem item : this.bookmarkItems) { + if (item.equalsRecipe(recipeId, targetItem.groupId) && item.factor > 0) { + item.amount = item.factor * multiplier; + } + } + + } else { + + for (BookmarkItem item : this.bookmarkItems) { + if (targetItem.equals(item)) { + item.amount = item.factor * shiftMultiplier(item.getMultiplier(), shift, 0); + break; + } + } + + } + + this.onItemsChanged(); + } + + private long shiftMultiplier(long multiplier, long shift, int minMultiplier) { + final long currentMultiplier = ((multiplier + shift) / shift) * shift; + return Math.min( + Integer.MAX_VALUE, + currentMultiplier <= 0 && multiplier > 1 ? 1 : Math.max(minMultiplier, currentMultiplier)); + } + + private long gcd(long a, long b) { + return b == 0 ? a : gcd(b, a % b); + } + + @Override + protected void beforeDrawItems(int mousex, int mousey, BookmarkMouseContext mouseContext) { + + if (LayoutManager.bookmarkPanel.inEditingState()) { + final SortableGroup sortableGroup = LayoutManager.bookmarkPanel.sortableGroup; + final SortableItem sortableItem = LayoutManager.bookmarkPanel.sortableItem; + final List borders = new ArrayList<>(); + + for (BookmarksGridSlot slot : getMask()) { + Rectangle4i rect = getSlotRect(slot.slotIndex); + + if (sortableGroup != null && slot.getGroupId() == sortableGroup.groupId) { + NEIClientUtils.drawRect(rect.x, rect.y, rect.w, rect.h, DRAG_COLOR); + } else if (sortableItem != null && sortableItem.items.contains(slot.itemIndex)) { + NEIClientUtils.drawRect(rect.x, rect.y, rect.w, rect.h, DRAG_COLOR); + borders.add(slot); + } else { + borders.add(slot); + } + + } + + for (BookmarksGridSlot slot : borders) { + slot.drawBorder(getSlotRect(slot.slotIndex)); + } + + } else { + super.beforeDrawItems(mousex, mousey, mouseContext); + } + + } + + @Override + protected void afterDrawItems(int mousex, int mousey, BookmarkMouseContext mouseContext) { + + if (LayoutManager.bookmarkPanel.inEditingState()) { + final SortableGroup sortableGroup = LayoutManager.bookmarkPanel.sortableGroup; + final SortableItem sortableItem = LayoutManager.bookmarkPanel.sortableItem; + + for (BookmarksGridSlot slot : getMask()) { + if ((sortableGroup == null || slot.getGroupId() != sortableGroup.groupId) + && (sortableItem == null || !sortableItem.items.contains(slot.itemIndex))) { + slot.afterDraw(getSlotRect(slot.slotIndex), mouseContext); + } + } + } else { + super.afterDrawItems(mousex, mousey, mouseContext); + } + } + + @Override + protected void drawItems() { + final SortableGroup sortableGroup = LayoutManager.bookmarkPanel.sortableGroup; + final SortableItem sortableItem = LayoutManager.bookmarkPanel.sortableItem; + + for (BookmarksGridSlot slot : getMask()) { + + if ((sortableGroup == null || slot.getGroupId() != sortableGroup.groupId) + && (sortableItem == null || !sortableItem.items.contains(slot.itemIndex))) { + final BookmarkItem item = slot.getBookmarkItem(); + + if (this.animation.containsKey(item) && this.animation.get(item) < 1) { + final float currentScale = this.animation.get(item) + SCALE_SPEED; + + if (currentScale >= 1) { + this.animation.remove(item); + } else { + this.animation.put(item, currentScale); + } + + drawPoppingItem(slot, getSlotRect(slot.slotIndex), currentScale); + } else { + slot.drawItem(getSlotRect(slot.slotIndex)); + } + + } + + } + + } + + protected void drawPoppingItem(BookmarksGridSlot slot, Rectangle4i rect, float currentScale) { + final float inverseScaleFactor = 1f / currentScale; + final float shiftX = (rect.x + (rect.w - rect.w * currentScale) / 2f) * inverseScaleFactor; + final float shiftY = (rect.y + (rect.h - rect.h * currentScale) / 2f) * inverseScaleFactor; + + GL11.glScalef(currentScale, currentScale, 1); + GL11.glTranslated(shiftX, shiftY, 0); + + slot.drawItem(new Rectangle4i(0, 0, rect.w, rect.h)); + + GL11.glTranslated(-1 * shiftX, -1 * shiftY, 0); + GL11.glScalef(inverseScaleFactor, inverseScaleFactor, 1); + } + +} diff --git a/src/main/java/codechicken/nei/bookmark/BookmarkGridGenerator.java b/src/main/java/codechicken/nei/bookmark/BookmarkGridGenerator.java new file mode 100644 index 00000000..09daaace --- /dev/null +++ b/src/main/java/codechicken/nei/bookmark/BookmarkGridGenerator.java @@ -0,0 +1,384 @@ +package codechicken.nei.bookmark; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.Comparator; +import java.util.HashMap; +import java.util.LinkedHashMap; +import java.util.List; +import java.util.Map; +import java.util.stream.Collectors; + +import codechicken.nei.BookmarkPanel.BookmarkViewMode; +import codechicken.nei.ItemSorter; +import codechicken.nei.bookmark.RecipeChainDetails.BookmarkChainItem; +import codechicken.nei.bookmark.RecipeChainDetails.CalculatedType; +import codechicken.nei.recipe.Recipe.RecipeId; +import codechicken.nei.recipe.StackInfo; + +public class BookmarkGridGenerator { + + protected final BookmarkGrid grid; + public List gridMask = null; + + public final Map rowToGroupId = new HashMap<>(); + public final Map itemToRecipe = new HashMap<>(); + + public final Map slotToItem = new LinkedHashMap<>(); + public final Map itemToSlot = new LinkedHashMap<>(); + + public final Map caclulatedItems = new HashMap<>(); + + public int pageCount = 0; + public int maxAbsoluteSlotIndex = 0; + + public BookmarkGridGenerator(BookmarkGrid grid) { + this.grid = grid; + } + + public int getRowGroupId(int rowIndex) { + final int absoluteRowIndex = rowIndex + (this.grid.getPage() - 1) * this.grid.getRows(); + return rowToGroupId.getOrDefault(absoluteRowIndex, BookmarkGrid.DEFAULT_GROUP_ID); + } + + public void generate() { + BookmarkItem prevBookmarkItem = null; + Map borderGrid = new HashMap<>(); + int gridColumns = this.grid.getColumns(); + int size = this.grid.size(); + int absoluteSlotIndex = -2; + int itemIndex = 0; + + this.pageCount = 0; + this.maxAbsoluteSlotIndex = 0; + this.gridMask = new ArrayList<>(); + this.itemToRecipe.clear(); + this.caclulatedItems.clear(); + this.slotToItem.clear(); + this.itemToSlot.clear(); + this.rowToGroupId.clear(); + + if (this.grid.isEmpty() || this.grid.getPerPage() == 0) { + return; + } + + while (itemIndex < size && absoluteSlotIndex != -1) { + final BookmarkItem bookmarkItem = this.grid.getBookmarkItem(itemIndex); + final BookmarkGroup group = this.grid.getGroup(bookmarkItem.groupId); + + if (group.crafting != null && (!group.crafting.calculatedItems.containsKey(itemIndex) + || bookmarkItem.recipeId != null && !bookmarkItem.recipeId + .equals(group.crafting.itemToRecipe.getOrDefault(itemIndex, bookmarkItem.recipeId)))) { + itemIndex++; + } else if (group.collapsed) { + final List results = getGroupResults(bookmarkItem.groupId, itemIndex, size); + final String groupKey = "group:" + bookmarkItem.groupId; + + for (int index = 0; index < results.size(); index++) { + final int collapsedItemIndex = results.get(index); + final BookmarkItem item = this.grid.getBookmarkItem(collapsedItemIndex); + final int nextSlotIndex = nextSlotIndex( + absoluteSlotIndex + 1, + prevBookmarkItem, + item, + BookmarkViewMode.DEFAULT); + + if (nextSlotIndex == -1 || index > 0 && (nextSlotIndex % gridColumns) == 0) { + break; + } + + prevBookmarkItem = item; + absoluteSlotIndex = nextSlotIndex; + this.slotToItem.put(absoluteSlotIndex, collapsedItemIndex); + this.itemToSlot.put(collapsedItemIndex, absoluteSlotIndex); + this.caclulatedItems.put(collapsedItemIndex, getItem(group, collapsedItemIndex)); + borderGrid.put(absoluteSlotIndex, groupKey); + } + + while (itemIndex < size && this.grid.getBookmarkItem(itemIndex).groupId == bookmarkItem.groupId) { + itemIndex++; + } + + } else if (group.crafting != null && group.viewMode == BookmarkViewMode.DEFAULT) { + final List results = getGroupResults(bookmarkItem.groupId, itemIndex, size); + List shadows; + + for (int index = 0; index < results.size() && absoluteSlotIndex != -1; index++) { + final int collapsedItemIndex = results.get(index); + final BookmarkItem item = this.grid.getBookmarkItem(collapsedItemIndex); + + if ((absoluteSlotIndex = nextSlotIndex( + absoluteSlotIndex + 1, + prevBookmarkItem, + item, + BookmarkViewMode.DEFAULT)) != -1) { + prevBookmarkItem = item; + this.slotToItem.put(absoluteSlotIndex, collapsedItemIndex); + this.itemToSlot.put(collapsedItemIndex, absoluteSlotIndex); + this.caclulatedItems.put(collapsedItemIndex, getItem(group, collapsedItemIndex)); + + if (!(shadows = getShadows(group, item.recipeId)).isEmpty()) { + borderGrid.put(absoluteSlotIndex, "recipe:" + shadows.get(0)); + } + } + } + + while (itemIndex < size && this.grid.getBookmarkItem(itemIndex).groupId == bookmarkItem.groupId) { + itemIndex++; + } + + } else if (group.crafting != null && group.crafting.itemToRecipe.values().contains(bookmarkItem.recipeId)) { + absoluteSlotIndex = generateCollapsedRecipe(prevBookmarkItem, itemIndex, absoluteSlotIndex, borderGrid); + prevBookmarkItem = bookmarkItem; + + while (itemIndex < size && bookmarkItem.equalsRecipe(this.grid.getBookmarkItem(itemIndex))) { + itemIndex++; + } + + } else if ((absoluteSlotIndex = nextSlotIndex( + absoluteSlotIndex + 1, + prevBookmarkItem, + bookmarkItem, + group.viewMode)) != -1) { + prevBookmarkItem = bookmarkItem; + this.slotToItem.put(absoluteSlotIndex, itemIndex); + this.itemToSlot.put(itemIndex, absoluteSlotIndex); + this.caclulatedItems.put(itemIndex, getItem(group, itemIndex)); + itemIndex++; + } + } + + this.maxAbsoluteSlotIndex = this.slotToItem.keySet().stream().mapToInt(index -> index).max().orElse(-1) + 1; + generateGroups(); + splitGridMask(); + calculateGroupBorders(borderGrid); + } + + private List getGroupResults(int groupId, int itemIndex, int size) { + final BookmarkGroup group = this.grid.getGroup(groupId); + final List results = new ArrayList<>(); + + while (itemIndex < size) { + final BookmarkItem bookmarkItem = this.grid.getBookmarkItem(itemIndex); + + if (bookmarkItem == null || bookmarkItem.groupId != groupId) { + break; + } + + if (!bookmarkItem.isIngredient + && (group.crafting == null || group.crafting.calculatedItems.containsKey(itemIndex))) { + results.add(itemIndex); + } + + itemIndex++; + } + + return results; + } + + private int generateCollapsedRecipe(BookmarkItem prevBookmarkItem, int itemIndex, int absoluteSlotIndex, + Map borderGrid) { + final BookmarkItem bookmarkItem = this.grid.getBookmarkItem(itemIndex); + final BookmarkGroup group = this.grid.getGroup(bookmarkItem.groupId); + final List shadows = getShadows(group, bookmarkItem.recipeId); + final String borderId = "recipe:" + shadows.get(0); + + shadows.sort(generateSortingComparator(group.crafting.calculatedItems)); + + for (int shadowIndex : shadows) { + final boolean isIngredient = shadows.get(0) != shadowIndex + || group.crafting.calculatedItems.get(shadowIndex).calculatedType == CalculatedType.INGREDIENT; + final BookmarkItem shadowBookmarkItem = BookmarkItem.of( + bookmarkItem.groupId, + bookmarkItem.itemStack, + 1, + bookmarkItem.recipeId, + isIngredient, + Collections.emptyMap()); + + if ((absoluteSlotIndex = nextSlotIndex( + absoluteSlotIndex + 1, + prevBookmarkItem, + shadowBookmarkItem, + group.viewMode)) != -1) { + this.slotToItem.put(absoluteSlotIndex, shadowIndex); + this.itemToSlot.put(shadowIndex, absoluteSlotIndex); + this.caclulatedItems.put(shadowIndex, getItem(group, shadowIndex)); + this.itemToRecipe.put(shadowIndex, bookmarkItem.recipeId); + borderGrid.put(absoluteSlotIndex, borderId); + prevBookmarkItem = shadowBookmarkItem; + } + + } + + return absoluteSlotIndex; + } + + private List getShadows(BookmarkGroup group, RecipeId recipeId) { + return group.crafting.itemToRecipe.entrySet().stream().filter(entry -> recipeId.equals(entry.getValue())) + .map(entry -> entry.getKey()).collect(Collectors.toList()); + } + + private int nextSlotIndex(int index, BookmarkItem previousMeta, BookmarkItem meta, BookmarkViewMode viewMode) { + int gridColumns = this.grid.getColumns(); + int maxIndex = this.grid.getRows() * gridColumns; + int j = 0; + + index = Math.max(0, index); + + while (j++ < maxIndex) { + boolean isFirstColumn = (index % gridColumns) == 0; + + if (this.grid.isInvalidSlot(index % maxIndex)) { + index++; + } else if (!isFirstColumn && (previousMeta == null || previousMeta.groupId != meta.groupId)) { + // new group must start on a new line + index++; + } else if (viewMode == BookmarkViewMode.DEFAULT) { + return index; + } else if (isFirstColumn && (meta.recipeId == null || previousMeta == null + || previousMeta.groupId != meta.groupId + || !meta.recipeId.equals(previousMeta.recipeId) + || index + 1 < maxIndex && this.grid.isInvalidSlot((index + 1) % maxIndex))) { + // In first column must be an item without recipe, a recipe result, or an ingredient + // if the second column is occupied + return index; + } else + if (!isFirstColumn && meta.recipeId != null && meta.recipeId.equals(previousMeta.recipeId)) { + return index; + } else { + index++; + } + + } + + return -1; + } + + private void generateGroups() { + int gridColumns = this.grid.getColumns(); + int previousGroupId = BookmarkGrid.DEFAULT_GROUP_ID; + + for (int absoluteSlotIndex = 0; absoluteSlotIndex < this.maxAbsoluteSlotIndex; absoluteSlotIndex++) { + int absoluteRowIndex = absoluteSlotIndex / gridColumns; + if (this.rowToGroupId.getOrDefault(absoluteRowIndex, BookmarkGrid.DEFAULT_GROUP_ID) + == BookmarkGrid.DEFAULT_GROUP_ID) { + int groupId = BookmarkGrid.DEFAULT_GROUP_ID; + + if (this.slotToItem.containsKey(absoluteSlotIndex)) { + groupId = this.caclulatedItems.get(this.slotToItem.get(absoluteSlotIndex)).getItem().groupId; + } + + this.rowToGroupId.put(absoluteRowIndex, groupId); + + if (groupId != BookmarkGrid.DEFAULT_GROUP_ID && previousGroupId == (previousGroupId = groupId)) { + while ((--absoluteRowIndex) >= 0 + && this.rowToGroupId.get(absoluteRowIndex) == BookmarkGrid.DEFAULT_GROUP_ID) { + this.rowToGroupId.put(absoluteRowIndex, groupId); + } + } + + } + } + + } + + private Comparator generateSortingComparator(Map calculatedItems) { + return Comparator.comparingInt((Integer index) -> calculatedItems.get(index).calculatedType.toInt()) + .thenComparing(index -> StackInfo.getFluid(calculatedItems.get(index).getItem().itemStack) != null) + .thenComparing( + (i1, i2) -> ItemSorter.instance.compare( + calculatedItems.get(i1).getItem().itemStack, + calculatedItems.get(i2).getItem().itemStack)); + } + + private void splitGridMask() { + int gridColumns = this.grid.getColumns(); + int rows = this.grid.getRows(); + int perPage = rows * gridColumns; + + this.pageCount = (int) Math.ceil(this.maxAbsoluteSlotIndex / (float) perPage); + int page = Math.max(0, Math.min(this.grid.getPage(), this.pageCount) - 1); + + for (int absoluteSlotIndex = page * perPage; absoluteSlotIndex + < Math.min(this.maxAbsoluteSlotIndex, (page + 1) * perPage); absoluteSlotIndex++) { + if (this.slotToItem.get(absoluteSlotIndex) != null) { + this.gridMask + .add(getBookmarkGridSlot(absoluteSlotIndex % perPage, this.slotToItem.get(absoluteSlotIndex))); + } + } + } + + private BookmarkChainItem getItem(BookmarkGroup group, int itemIndex) { + if (group.crafting != null) { + return group.crafting.calculatedItems.get(itemIndex); + } else { + BookmarkChainItem item = BookmarkChainItem.of(this.grid.getBookmarkItem(itemIndex)); + item.setRealAmount(item.getShiftAmount()); + return item; + } + } + + private BookmarksGridSlot getBookmarkGridSlot(int slotIndex, int itemIndex) { + final BookmarkChainItem chainItem = this.caclulatedItems.get(itemIndex); + final BookmarkGroup group = this.grid.getGroup(chainItem.getItem().groupId); + + if (group.crafting == null) { + return new BookmarksGridSlot( + slotIndex, + itemIndex, + chainItem.getRealAmount(), + 0, + 0, + chainItem.getItem(), + null, + group); + } else { + return new BookmarksGridSlot( + slotIndex, + itemIndex, + chainItem.getRealAmount(), + chainItem.getShiftAmount(), + chainItem.getCalculatedAmount(), + chainItem.getItem(), + chainItem.calculatedType, + group); + } + } + + protected void calculateGroupBorders(Map borderGrid) { + int gridColumns = this.grid.getColumns(); + int page = Math.max(1, Math.min(this.grid.getPage(), this.pageCount)) - 1; + int shiftIndex = page * this.grid.getRows() * gridColumns; + + for (BookmarksGridSlot slot : this.gridMask) { + if (borderGrid.get(shiftIndex + slot.slotIndex) == null) { + continue; + } + + String groupIndex = borderGrid.get(shiftIndex + slot.slotIndex); + int column = slot.slotIndex % gridColumns; + int row = slot.slotIndex / gridColumns; + int prevSlotIndex = shiftIndex + (row - 1) * gridColumns + column; + int nextSlotIndex = shiftIndex + (row + 1) * gridColumns + column; + BookmarkGroup group = slot.getGroup(); + + if (group.crafting == null) { + slot.borderColor = BookmarkGrid.GROUP_NONE_COLOR; + } else if (group.collapsed) { + slot.borderColor = BookmarkGrid.GROUP_CHAIN_COLOR; + } else { + slot.borderColor = BookmarkGrid.RECIPE_COLOR; + } + + slot.borderLeft = column == 0 || !groupIndex.equals(borderGrid.get(shiftIndex + slot.slotIndex - 1)); + slot.borderRight = column + 1 == gridColumns + || !groupIndex.equals(borderGrid.get(shiftIndex + slot.slotIndex + 1)); + + slot.borderTop = !groupIndex.equals(borderGrid.get(prevSlotIndex)); + slot.borderBottom = !groupIndex.equals(borderGrid.get(nextSlotIndex)); + } + + } + +} diff --git a/src/main/java/codechicken/nei/bookmark/BookmarkGroup.java b/src/main/java/codechicken/nei/bookmark/BookmarkGroup.java new file mode 100644 index 00000000..ac75001d --- /dev/null +++ b/src/main/java/codechicken/nei/bookmark/BookmarkGroup.java @@ -0,0 +1,58 @@ +package codechicken.nei.bookmark; + +import java.util.HashSet; +import java.util.Set; + +import codechicken.nei.BookmarkPanel.BookmarkViewMode; +import codechicken.nei.recipe.Recipe.RecipeId; + +public class BookmarkGroup { + + public RecipeChainDetails crafting = null; + public BookmarkViewMode viewMode; + public boolean collapsed = false; + public Set collapsedRecipes = new HashSet<>(); + + public BookmarkGroup(BookmarkViewMode viewMode) { + this.viewMode = viewMode; + } + + public BookmarkGroup(BookmarkViewMode viewMode, boolean crafting) { + this.viewMode = viewMode; + setCraftingMode(crafting); + } + + public void toggleViewMode() { + if (this.viewMode == BookmarkViewMode.DEFAULT) { + this.viewMode = BookmarkViewMode.TODO_LIST; + } else { + this.viewMode = BookmarkViewMode.DEFAULT; + } + } + + public void setCraftingMode(boolean crafting) { + if ((this.crafting != null) != crafting) { + this.crafting = crafting ? new RecipeChainDetails() : null; + } + } + + public void toggleCraftingMode() { + setCraftingMode(this.crafting == null); + } + + public void toggleCollapsedState() { + this.collapsed = !this.collapsed; + } + + public void toggleCollapsedRecipe(RecipeId recipeId) { + if (this.collapsedRecipes.contains(recipeId)) { + this.collapsedRecipes.remove(recipeId); + } else { + this.collapsedRecipes.add(recipeId); + } + } + + public BookmarkGroup copy() { + return new BookmarkGroup(this.viewMode); + } +} diff --git a/src/main/java/codechicken/nei/bookmark/BookmarkItem.java b/src/main/java/codechicken/nei/bookmark/BookmarkItem.java new file mode 100644 index 00000000..0722af36 --- /dev/null +++ b/src/main/java/codechicken/nei/bookmark/BookmarkItem.java @@ -0,0 +1,191 @@ +package codechicken.nei.bookmark; + +import java.util.Collections; +import java.util.HashMap; +import java.util.Map; +import java.util.Objects; + +import net.minecraft.item.ItemStack; +import net.minecraft.nbt.NBTTagCompound; +import net.minecraftforge.fluids.FluidStack; + +import codechicken.nei.recipe.Recipe; +import codechicken.nei.recipe.Recipe.RecipeId; +import codechicken.nei.recipe.Recipe.RecipeIngredient; +import codechicken.nei.recipe.StackInfo; + +public class BookmarkItem { + + public int groupId; + + public long amount = 0L; + public final int fluidCellAmount; + public ItemStack itemStack; + public Map permutations; + + public long factor; + public RecipeId recipeId; + public boolean isIngredient = false; + + protected BookmarkItem(int groupId, long amount, int fluidCellAmount, ItemStack itemStack, + Map permutations, long factor, RecipeId recipeId, boolean isIngredient) { + this.groupId = groupId; + + this.amount = amount; + this.fluidCellAmount = fluidCellAmount; + this.itemStack = itemStack; + this.permutations = permutations; + + this.factor = factor; + this.recipeId = recipeId; + this.isIngredient = isIngredient; + } + + public static BookmarkItem of(int groupId, ItemStack stack, long factor, RecipeId recipeId, boolean isIngredient, + Map permutations) { + final FluidStack fluidStack = StackInfo.getFluid(stack); + int fluidCellAmount = 1; + long amount; + + if (fluidStack != null) { + amount = fluidStack.amount * (long) Math.max(0, stack.stackSize); + fluidCellAmount = Math.max(1, StackInfo.isFluidContainer(stack) ? fluidStack.amount : 1); + } else { + amount = StackInfo.getAmount(stack); + } + + return new BookmarkItem( + groupId, + amount, + fluidCellAmount, + stack, + permutations, + factor * fluidCellAmount, + recipeId, + isIngredient); + } + + public static BookmarkItem of(int groupId, ItemStack stack, long factor, RecipeId recipeId, boolean isIngredient) { + return of(groupId, stack, factor, recipeId, isIngredient, generatePermutations(stack, recipeId, isIngredient)); + } + + public static BookmarkItem of(int groupId, ItemStack stack) { + final NBTTagCompound nbTag = StackInfo.itemStackToNBT(stack); + final long factor = nbTag.hasKey("gtFluidName") ? Math.min(144, nbTag.getInteger("Count")) : 1; + return BookmarkItem + .of(groupId, stack, factor, null, false, Collections.singletonMap(getItemGUID(stack), stack)); + } + + public BookmarkItem copyWithAmount(long amount) { + return new BookmarkItem( + this.groupId, + amount, + this.fluidCellAmount, + this.itemStack, + this.permutations, + this.factor, + this.recipeId, + this.isIngredient); + } + + public BookmarkItem copy() { + return copyWithAmount(this.amount); + } + + public static Map generatePermutations(ItemStack stack, RecipeId recipeId, + boolean isIngredient) { + + if (isIngredient && recipeId != null) { + return generatePermutations(stack, Recipe.of(recipeId)); + } + + return Collections.singletonMap(getItemGUID(stack), stack); + } + + public static Map generatePermutations(ItemStack stack, Recipe recipe) { + + if (recipe != null) { + final RecipeIngredient ingr = recipe.getIngredients().stream() + .filter(ingredient -> ingredient.contains(stack)).findAny().orElse(null); + + if (ingr != null) { + final Map permutations = new HashMap<>(); + for (ItemStack ingrStack : ingr.getPermutations()) { + permutations.put(getItemGUID(ingrStack), ingrStack); + } + return permutations; + } + } + + return Collections.singletonMap(getItemGUID(stack), stack); + } + + private static String getItemGUID(ItemStack stack) { + final FluidStack fluidStack = StackInfo.getFluid(stack); + + if (fluidStack != null) { + return fluidStack.getFluid().getName() + ":" + fluidStack.tag; + } else { + return StackInfo.getItemStackGUID(stack); + } + } + + public boolean containsItems(BookmarkItem item) { + return this.permutations.keySet().stream().anyMatch(item.permutations::containsKey); + } + + public ItemStack getItemStack() { + return getItemStack(this.amount); + } + + public ItemStack getItemStack(long amount) { + return StackInfo.withAmount(this.itemStack, getStackSize(amount)); + } + + public long getStackSize() { + return getStackSize(this.amount); + } + + public long getStackSize(long amount) { + return (long) (this.factor > 0 ? Math.ceil(amount / (double) this.fluidCellAmount) : 0); + } + + public long getMultiplier() { + return getMultiplier(this.amount); + } + + public long getMultiplier(long amount) { + return (long) (this.factor > 0 ? Math.ceil(amount / (double) this.factor) : 0); + } + + public long getFactor() { + return this.factor / this.fluidCellAmount; + } + + public boolean equalsRecipe(BookmarkItem meta) { + return equalsRecipe(meta.recipeId, meta.groupId); + } + + public boolean equalsRecipe(RecipeId recipeId, int groupId) { + return groupId == this.groupId && recipeId != null && recipeId.equals(this.recipeId); + } + + @Override + public int hashCode() { + return Objects.hash(this.groupId, this.fluidCellAmount, this.isIngredient, this.recipeId); + } + + @Override + public boolean equals(Object object) { + + if (object instanceof BookmarkItem item) { + return this.groupId == item.groupId && this.isIngredient == item.isIngredient + && this.fluidCellAmount == item.fluidCellAmount + && StackInfo.equalItemAndNBT(this.itemStack, item.itemStack, true) + && (this.recipeId == item.recipeId || this.recipeId != null && this.recipeId.equals(item.recipeId)); + } + + return false; + } + +} diff --git a/src/main/java/codechicken/nei/bookmark/BookmarkStorage.java b/src/main/java/codechicken/nei/bookmark/BookmarkStorage.java new file mode 100644 index 00000000..213a488a --- /dev/null +++ b/src/main/java/codechicken/nei/bookmark/BookmarkStorage.java @@ -0,0 +1,366 @@ +package codechicken.nei.bookmark; + +import java.io.File; +import java.io.FileInputStream; +import java.io.FileOutputStream; +import java.io.IOException; +import java.nio.charset.StandardCharsets; +import java.util.ArrayList; +import java.util.Collections; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +import net.minecraft.item.ItemStack; +import net.minecraft.nbt.NBTTagCompound; + +import org.apache.commons.io.IOUtils; + +import com.google.gson.JsonArray; +import com.google.gson.JsonElement; +import com.google.gson.JsonObject; +import com.google.gson.JsonParser; +import com.google.gson.JsonPrimitive; +import com.google.gson.JsonSyntaxException; + +import codechicken.nei.BookmarkPanel.BookmarkViewMode; +import codechicken.nei.NEIClientConfig; +import codechicken.nei.recipe.Recipe.RecipeId; +import codechicken.nei.recipe.StackInfo; +import codechicken.nei.util.NBTJson; + +public class BookmarkStorage { + + protected List namespaces = new ArrayList<>(); + protected int activeNamespaceIndex = 0; + protected File bookmarkFile = null; + + public BookmarkStorage() { + this.namespaces.add(new BookmarkGrid()); + } + + public boolean prevNamespace() { + getNamespaceSize(); + removeEmptyNamespaces(); + + if (this.activeNamespaceIndex == 0) { + setNamespace(getNamespaceSize() - 1); + } else { + setNamespace(this.activeNamespaceIndex - 1); + } + + return true; + } + + public boolean nextNamespace() { + + if (removeEmptyNamespaces()) { + return true; + } + + if (this.activeNamespaceIndex == getNamespaceSize() - 1) { + setNamespace(0); + } else { + setNamespace(this.activeNamespaceIndex + 1); + } + + return true; + } + + private void setNamespace(int namespaceIndex) { + this.activeNamespaceIndex = Math.min(namespaceIndex, this.namespaces.size() - 1); + BookmarkGrid grid = this.namespaces.get(this.activeNamespaceIndex); + + if (grid.size() == 0 && this.activeNamespaceIndex > 0) { + grid.setViewMode( + BookmarkGrid.DEFAULT_GROUP_ID, + this.namespaces.get(this.activeNamespaceIndex - 1).getViewMode(BookmarkGrid.DEFAULT_GROUP_ID)); + } + + } + + public int getNamespaceSize() { + + if (this.namespaces.get(this.namespaces.size() - 1).size() > 0) { + this.namespaces.add(new BookmarkGrid()); + } else if (this.activeNamespaceIndex == this.namespaces.size() - 2 && getActiveGrid().size() == 0) { + this.namespaces.remove(this.namespaces.size() - 1); + } + + return this.namespaces.size(); + } + + public boolean isEmpty() { + return this.namespaces.size() <= 1 && getActiveGrid().isEmpty(); + } + + public int getActiveIndex() { + return this.activeNamespaceIndex; + } + + public BookmarkGrid getActiveGrid() { + return this.namespaces.get(this.activeNamespaceIndex); + } + + private boolean removeEmptyNamespaces() { + + if (this.activeNamespaceIndex != this.namespaces.size() - 1 && getActiveGrid().size() == 0) { + this.namespaces.remove(this.activeNamespaceIndex); + setNamespace(this.activeNamespaceIndex); + return true; + } + + return false; + } + + public void load(File bookmarkFile) { + + if (bookmarkFile.equals(this.bookmarkFile)) { + return; + } + + List itemStrings = Collections.emptyList(); + + if (bookmarkFile.exists()) { + try (FileInputStream reader = new FileInputStream(bookmarkFile)) { + NEIClientConfig.logger.info("Loading bookmarks from file {}", bookmarkFile); + itemStrings = IOUtils.readLines(reader, StandardCharsets.UTF_8); + } catch (IOException e) { + NEIClientConfig.logger.error("Failed to load bookmarks from file {}", bookmarkFile, e); + return; + } + } + + final JsonParser parser = new JsonParser(); + final List namespaces = new ArrayList<>(); + NBTTagCompound navigation = new NBTTagCompound(); + namespaces.add(new BookmarkGrid()); + BookmarkGrid grid = namespaces.get(0); + + if (NEIClientConfig.world.nbt.hasKey("bookmark")) { + navigation = NEIClientConfig.world.nbt.getCompoundTag("bookmark"); + } + + for (String itemStr : itemStrings) { + + try { + + if (itemStr.isEmpty()) { + itemStr = "; {}"; + } + + if (itemStr.startsWith("; ")) { + JsonObject settings = parser.parse(itemStr.substring(2)).getAsJsonObject(); + + if (grid.size() > 0) { + // do not create empty namespaces + grid = new BookmarkGrid(); + namespaces.add(grid); + } + + if (navigation.hasKey("namespacePage." + (namespaces.size() - 1))) { + grid.setPage(navigation.getInteger("namespacePage." + (namespaces.size() - 1)));; + } + + if (settings.get("viewmode") != null) { + grid.groups.get(BookmarkGrid.DEFAULT_GROUP_ID).viewMode = BookmarkViewMode + .valueOf(settings.get("viewmode").getAsString()); + } else if (settings.get("groups") instanceof JsonObject jsonObject) { + BookmarkGroup group; + + for (Map.Entry jsonEntry : jsonObject.entrySet()) { + if (jsonEntry.getValue() instanceof JsonObject value) { + group = new BookmarkGroup( + value.has("viewmode") + ? BookmarkViewMode.valueOf(value.get("viewmode").getAsString()) + : BookmarkViewMode.DEFAULT, + value.has("crafting") && value.get("crafting").getAsBoolean()); + + group.collapsed = value.has("collapsed") && value.get("collapsed").getAsBoolean(); + + if (value.has("collapsedRecipes")) { + for (JsonElement recipeId : value.get("collapsedRecipes").getAsJsonArray()) { + if (recipeId instanceof JsonObject item) { + group.collapsedRecipes.add(RecipeId.of(item)); + } + } + } + + grid.groups.put(Integer.valueOf(jsonEntry.getKey()), group); + } + } + } + + continue; + } + + if (!addItemToGrid(grid, parser.parse(itemStr).getAsJsonObject())) { + NEIClientConfig.logger.warn( + "Failed to load bookmarked ItemStack from json string, the item no longer exists:\n{}", + itemStr); + } + + } catch (IllegalArgumentException | JsonSyntaxException | IllegalStateException e) { + NEIClientConfig.logger.error("Failed to load bookmarked ItemStack from json string:\n{}", itemStr); + } + } + + prepareOldRecipeFormat(namespaces); + + for (BookmarkGrid gr : namespaces) { + gr.onItemsChanged(); + } + + this.namespaces = namespaces; + + if (navigation.hasKey("namespaceIndex")) { + setNamespace(navigation.getInteger("namespaceIndex")); + } else { + setNamespace(0); + } + + this.bookmarkFile = bookmarkFile; + } + + private boolean addItemToGrid(BookmarkGrid grid, JsonObject jsonObject) { + NBTTagCompound itemStackNBT; + + if (jsonObject.get("item") != null) { + itemStackNBT = (NBTTagCompound) NBTJson.toNbt(jsonObject.get("item")); + } else { // old format + itemStackNBT = (NBTTagCompound) NBTJson.toNbt(jsonObject); + } + + ItemStack itemStack = StackInfo.loadFromNBT(itemStackNBT); + + if (itemStack != null) { + RecipeId recipeId = null; + boolean isFluid = itemStackNBT.hasKey("gtFluidName"); + int groupId = jsonObject.has("groupId") ? jsonObject.get("groupId").getAsInt() + : BookmarkGrid.DEFAULT_GROUP_ID; + int factor = jsonObject.has("factor") ? Math.abs(jsonObject.get("factor").getAsInt()) : (isFluid ? 144 : 1); + boolean isIngredient = jsonObject.has("ingredient") && jsonObject.get("ingredient").getAsBoolean(); + + if (jsonObject.get("recipeId") instanceof JsonObject recipeJson) { + recipeId = RecipeId.of(recipeJson); + } + + if (!grid.groups.containsKey(groupId)) { + groupId = BookmarkGrid.DEFAULT_GROUP_ID; + } + + grid.addItem(BookmarkItem.of(groupId, itemStack, factor, recipeId, isIngredient), false); + } + + return itemStack != null; + } + + private void prepareOldRecipeFormat(List namespaces) { + + for (BookmarkGrid grid : namespaces) { + if (grid.bookmarkItems.stream() + .noneMatch(item -> item.recipeId != null && item.recipeId.getResult() == null)) { + continue; + } + + final Map recipes = new HashMap<>(); + + for (BookmarkItem item : grid.bookmarkItems) { + if (item.recipeId != null && !item.isIngredient && item.recipeId.getResult() == null) { + final JsonObject recipeJson = item.recipeId.toJsonObject(); + final String recipeGUID = item.groupId + ":" + NBTJson.toJson(recipeJson); + + if (!recipes.containsKey(recipeGUID)) { + recipeJson.add("result", NBTJson.toJsonObject(StackInfo.itemStackToNBT(item.itemStack))); + recipes.put(recipeGUID, item.recipeId = RecipeId.of(recipeJson)); + } + } + } + + for (BookmarkItem item : grid.bookmarkItems) { + if (item.recipeId != null && item.recipeId.getResult() == null) { + final JsonObject recipeJson = item.recipeId.toJsonObject(); + final String recipeGUID = item.groupId + ":" + NBTJson.toJson(recipeJson); + + if (recipes.containsKey(recipeGUID)) { + item.recipeId = recipes.get(recipeGUID).copy(); + item.permutations = BookmarkItem + .generatePermutations(item.itemStack, item.recipeId, item.isIngredient); + } else { + item.recipeId = null; + } + } + } + } + } + + public void save() { + + if (this.bookmarkFile == null) { + return; + } + + List strings = new ArrayList<>(); + NBTTagCompound navigation = new NBTTagCompound(); + navigation.setInteger("namespaceIndex", this.activeNamespaceIndex); + + for (int grpIdx = 0; grpIdx < namespaces.size(); grpIdx++) { + BookmarkGrid grid = namespaces.get(grpIdx); + JsonObject settings = new JsonObject(); + JsonObject groups = new JsonObject(); + + for (int groupId : grid.groups.keySet()) { + BookmarkGroup group = grid.groups.get(groupId); + JsonObject groupJson = new JsonObject(); + groupJson.add("viewmode", new JsonPrimitive(group.viewMode.toString())); + groupJson.add("crafting", new JsonPrimitive(group.crafting != null)); + groupJson.add("collapsed", new JsonPrimitive(group.collapsed)); + + if (!group.collapsedRecipes.isEmpty()) { + JsonArray collapsedRecipes = new JsonArray(); + for (RecipeId recipeId : group.collapsedRecipes) { + collapsedRecipes.add(recipeId.toJsonObject()); + } + groupJson.add("collapsedRecipes", collapsedRecipes); + } + + groups.add(String.valueOf(groupId), groupJson); + } + + settings.add("groups", groups); + strings.add("; " + NBTJson.toJson(settings)); + + navigation.setInteger("namespacePage." + grpIdx, grid.getPage() - 1); + + for (BookmarkItem item : grid.bookmarkItems) { + try { + JsonObject row = new JsonObject(); + + row.add("item", NBTJson.toJsonObject(StackInfo.itemStackToNBT(item.getItemStack()))); + row.add("factor", new JsonPrimitive(item.getFactor())); + row.add("ingredient", new JsonPrimitive(item.isIngredient)); + + if (item.groupId != BookmarkGrid.DEFAULT_GROUP_ID) { + row.add("groupId", new JsonPrimitive(item.groupId)); + } + + if (item.recipeId != null) { + row.add("recipeId", item.recipeId.toJsonObject()); + } + + strings.add(NBTJson.toJson(row)); + } catch (JsonSyntaxException e) { + NEIClientConfig.logger.error("Failed to stringify bookmarked ItemStack to json string"); + } + } + } + + try (FileOutputStream output = new FileOutputStream(bookmarkFile)) { + IOUtils.writeLines(strings, "\n", output, StandardCharsets.UTF_8); + NEIClientConfig.world.nbt.setTag("bookmark", navigation); + } catch (IOException e) { + NEIClientConfig.logger.error("Filed to save bookmarks list to file {}", bookmarkFile, e); + } + } + +} diff --git a/src/main/java/codechicken/nei/bookmark/BookmarksGridSlot.java b/src/main/java/codechicken/nei/bookmark/BookmarksGridSlot.java new file mode 100644 index 00000000..c9ef5b6f --- /dev/null +++ b/src/main/java/codechicken/nei/bookmark/BookmarksGridSlot.java @@ -0,0 +1,322 @@ +package codechicken.nei.bookmark; + +import java.awt.Color; + +import net.minecraft.item.ItemStack; + +import codechicken.lib.vec.Rectangle4i; +import codechicken.nei.BookmarkPanel.BookmarkViewMode; +import codechicken.nei.ItemPanels; +import codechicken.nei.ItemsGrid.ItemsGridSlot; +import codechicken.nei.ItemsGrid.MouseContext; +import codechicken.nei.NEIClientConfig; +import codechicken.nei.NEIClientUtils; +import codechicken.nei.NEIClientUtils.Alignment; +import codechicken.nei.bookmark.BookmarkGrid.BookmarkMouseContext; +import codechicken.nei.bookmark.RecipeChainDetails.CalculatedType; +import codechicken.nei.recipe.Recipe.RecipeId; +import codechicken.nei.recipe.StackInfo; +import codechicken.nei.util.ReadableNumberConverter; + +public class BookmarksGridSlot extends ItemsGridSlot { + + protected enum ShownItemType { + REAL, + SHIFT, + CALCULATED + } + + protected static final float LINE_WIDTH = 0.75f; + + protected static final Color INGREDIENTS_COLOR = new Color(0x6645DA75, true); + + protected static final Color RESULTS_COLOR = new Color(0x9966CCFF, true); + protected static final Color RESULTS_BG_COLOR = new Color(0x3366CCFF, true); + protected static final Color RESULTS_BG_FLUID_COLOR = new Color(0x5566CCFF, true); + + protected static final Color REMAINDER_COLOR = new Color(0x55A033A0, true); + protected static final Color REMAINDER_BG_COLOR = new Color(0x33A033A0, true); + protected static final Color REMAINDER_BG_FLUID_COLOR = new Color(0x44A033A0, true); + + protected final ItemStack emptyItemStack; + protected final long realAmount; + protected final long shiftAmount; + protected final long calculatedAmount; + + protected final long realMultiplier; + protected final long calculatedMultiplier; + + protected final BookmarkGroup group; + protected final boolean isFluidDisplay; + protected final CalculatedType calculatedType; + protected final CalculatedType realType; + protected final BookmarkItem bookmarkItem; + + protected final boolean isOutputRecipe; + protected final boolean isFirstOutput; + protected final int rowIndex; + + public BookmarksGridSlot(int slotIndex, int itemIndex, long realAmount, long shiftAmount, long calculatedAmount, + BookmarkItem bookmarkItem, CalculatedType calculatedType, BookmarkGroup group) { + super( + slotIndex, + itemIndex, + calculatedType != null ? bookmarkItem.getItemStack(calculatedAmount) + : bookmarkItem.getItemStack(realAmount)); + + this.emptyItemStack = bookmarkItem.getItemStack(0); + this.realAmount = realAmount; + this.shiftAmount = shiftAmount; + this.calculatedAmount = calculatedAmount; + + this.group = group; + this.calculatedType = calculatedType; + this.bookmarkItem = bookmarkItem; + + this.isOutputRecipe = group.crafting != null && group.crafting.outputRecipes.contains(bookmarkItem.recipeId); + this.isFluidDisplay = StackInfo.itemStackToNBT(bookmarkItem.itemStack).hasKey("gtFluidName"); + + this.realMultiplier = bookmarkItem.getMultiplier(realAmount); + this.calculatedMultiplier = bookmarkItem.getMultiplier(calculatedAmount); + + if (this.group.crafting != null && this.group.crafting.calculatedItems.containsKey(this.itemIndex)) { + this.realType = this.group.crafting.calculatedItems.get(this.itemIndex).calculatedType + == CalculatedType.INGREDIENT ? CalculatedType.INGREDIENT : CalculatedType.RESULT; + } else { + this.realType = this.bookmarkItem.isIngredient + || this.group.crafting != null && this.bookmarkItem.recipeId == null ? CalculatedType.INGREDIENT + : CalculatedType.RESULT; + } + + final BookmarkGrid grid = ItemPanels.bookmarkPanel.getGrid(); + final BookmarkItem prevItem = grid.getCalculatedItem( + grid.gridGenerator.slotToItem.getOrDefault(grid.gridGenerator.itemToSlot.get(this.itemIndex) - 1, -1)); + this.isFirstOutput = this.itemIndex >= 0 && !this.bookmarkItem.isIngredient + && ((this.slotIndex % grid.getColumns()) == 0 || group.viewMode == BookmarkViewMode.DEFAULT) + && (prevItem == null || prevItem.isIngredient || !this.bookmarkItem.equalsRecipe(prevItem)); + this.rowIndex = this.slotIndex / grid.getColumns(); + } + + public long getMultiplier() { + return this.calculatedType != null ? this.calculatedMultiplier : this.realMultiplier; + } + + public BookmarkItem getBookmarkItem() { + return this.bookmarkItem; + } + + public RecipeId getRecipeId() { + return this.bookmarkItem.recipeId; + } + + public boolean isIngredient() { + return this.bookmarkItem.isIngredient; + } + + public BookmarkGroup getGroup() { + return this.group; + } + + public int getGroupId() { + return this.bookmarkItem.groupId; + } + + protected ShownItemType getShownItemType(BookmarkMouseContext mouseContext) { + + if (mouseContext != null) { + if (this.showShiftItem(mouseContext)) { + return ShownItemType.SHIFT; + } else if (this.showRealItem(mouseContext)) { + return ShownItemType.REAL; + } + } + + if (this.calculatedType != null) { + return ShownItemType.CALCULATED; + } + + return null; + } + + protected boolean showShiftItem(BookmarkMouseContext mouseContext) { + + if (!mouseContext.shiftKey || this.bookmarkItem.groupId != mouseContext.groupId) { + return false; + } + + if (this.group.crafting != null) { + return true; + } + + return this.bookmarkItem.equalsRecipe(mouseContext.recipeId, mouseContext.groupId); + } + + protected boolean showRealItem(BookmarkMouseContext mouseContext) { + + if (!mouseContext.controlKey || this.bookmarkItem.groupId != mouseContext.groupId) { + return false; + } + + if (this.slotIndex == mouseContext.slotIndex || this.group.collapsed) { + return true; + } + + if (mouseContext.recipeId == null && this.group.viewMode == BookmarkViewMode.TODO_LIST + && mouseContext.rowIndex == this.rowIndex) { + return true; + } + + if (this.bookmarkItem.equalsRecipe(mouseContext.recipeId, mouseContext.groupId)) { + return true; + } + + return this.group.crafting != null && mouseContext.recipeId != null + && mouseContext.recipeId.equals(this.group.crafting.itemToRecipe.get(this.itemIndex)); + } + + @Override + public void drawItem(Rectangle4i rect) { + super.drawItem(this.emptyItemStack, rect); + } + + @Override + public void beforeDraw(Rectangle4i rect, M mouseContext) { + final ShownItemType shownItemType = getShownItemType((BookmarkMouseContext) mouseContext); + boolean showMarker = NEIClientConfig.showRecipeMarkerMode() == 2; + CalculatedType calculatedType = null; + + if (shownItemType == ShownItemType.REAL) { + calculatedType = this.realType; + } else if (shownItemType == ShownItemType.SHIFT) { + + if (this.calculatedType != null) { + calculatedType = this.shiftAmount > 0 ? this.calculatedType : null; + showMarker = false; + } else if (group.crafting == null) { + calculatedType = this.realType; + } + + } + + if (!this.bookmarkItem.isIngredient && this.bookmarkItem.recipeId != null + && calculatedType == null + && showMarker + && (mouseContext == null || mouseContext.slotIndex != this.slotIndex)) { + drawMerkerBackground(rect); + } + + if (calculatedType == null || !drawBackground(rect, calculatedType)) { + super.beforeDraw(rect, mouseContext); + } + } + + protected boolean drawBackground(Rectangle4i rect, CalculatedType calculatedType) { + + switch (calculatedType) { + case INGREDIENT: + NEIClientUtils.drawRect(rect.x, rect.y, rect.w, rect.h, INGREDIENTS_COLOR); + break; + case RESULT: + NEIClientUtils.drawRect(rect.x, rect.y, rect.w, rect.h, RESULTS_COLOR); + break; + case REMAINDER: + NEIClientUtils.drawRect(rect.x, rect.y, rect.w, rect.h, REMAINDER_COLOR); + break; + default: + return false; + } + + return true; + } + + protected void drawMerkerBackground(Rectangle4i rect) { + Color color = this.isFluidDisplay ? RESULTS_BG_FLUID_COLOR : RESULTS_BG_COLOR; + + if (this.shiftAmount > 0 && this.calculatedType == CalculatedType.REMAINDER) { + color = this.isFluidDisplay ? REMAINDER_BG_FLUID_COLOR : REMAINDER_BG_COLOR; + } + + NEIClientUtils.drawRect(rect.x, rect.y, rect.w, rect.h, color); + } + + @Override + public void afterDraw(Rectangle4i rect, M mouseContext) { + final ShownItemType shownItemType = getShownItemType((BookmarkMouseContext) mouseContext); + + drawStackSize(rect, shownItemType); + + if (this.bookmarkItem.isIngredient || this.bookmarkItem.recipeId == null) { + return; + } + + if (this.isFirstOutput) { + if (shownItemType == ShownItemType.REAL) { + drawMarker( + rect, + "x" + ReadableNumberConverter.INSTANCE.toWideReadableForm(this.realMultiplier), + 0xFFFFFF); + } else if (shownItemType == ShownItemType.SHIFT) { + drawMarker(rect, "x" + ReadableNumberConverter.INSTANCE.toWideReadableForm(getMultiplier()), 0xFFFFFF); + } else if (this.isOutputRecipe || this.realMultiplier > 1) { + drawMarker( + rect, + "x" + ReadableNumberConverter.INSTANCE.toWideReadableForm(this.realMultiplier), + NEIClientConfig.getSetting("inventory.bookmarks.recipeMarkerColor").getHexValue()); + } else if (NEIClientConfig.showRecipeMarkerMode() == 1) { + drawMarker( + rect, + "R", + NEIClientConfig.getSetting("inventory.bookmarks.recipeMarkerColor").getHexValue()); + } + } else if (NEIClientConfig.showRecipeMarkerMode() == 1) { + drawMarker(rect, "R", NEIClientConfig.getSetting("inventory.bookmarks.recipeMarkerColor").getHexValue()); + } + + } + + protected void drawMarker(Rectangle4i rect, String text, int color) { + final float panelFactor = (rect.w - 2) / (DEFAULT_SLOT_SIZE - 2); + NEIClientUtils.drawNEIOverlayText( + text, + new Rectangle4i(rect.x + 1, rect.y + 1, rect.w - 2, rect.h - 2), + panelFactor, + color, + true, + Alignment.TopLeft); + } + + protected void drawStackSize(Rectangle4i rect, ShownItemType shownItemType) { + long amount = this.calculatedAmount; + + if (shownItemType == null || shownItemType == ShownItemType.REAL || this.calculatedType == null) { + amount = this.realAmount; + } else if (shownItemType == ShownItemType.SHIFT) { + amount = this.shiftAmount; + } + + if (amount > 0 || shownItemType == ShownItemType.REAL && this.bookmarkItem.factor > 0) { + final float panelFactor = (rect.w - 2) / (DEFAULT_SLOT_SIZE - 2); + final long stackSize = this.bookmarkItem.getStackSize(amount); + String amountString = ""; + + if (stackSize < 10_000) { + amountString = String.valueOf(stackSize); + } else { + amountString = ReadableNumberConverter.INSTANCE.toWideReadableForm(stackSize); + } + + if (this.isFluidDisplay) { + amountString += "L"; + } + + NEIClientUtils.drawNEIOverlayText( + amountString, + new Rectangle4i(rect.x + 1, rect.y + 1, rect.w - 2, rect.h - 2), + panelFactor, + 0xFFFFFF, + true, + this.isFluidDisplay ? Alignment.BottomLeft : Alignment.BottomRight); + } + } + +} diff --git a/src/main/java/codechicken/nei/bookmark/GroupingItem.java b/src/main/java/codechicken/nei/bookmark/GroupingItem.java new file mode 100644 index 00000000..6b5938db --- /dev/null +++ b/src/main/java/codechicken/nei/bookmark/GroupingItem.java @@ -0,0 +1,190 @@ +package codechicken.nei.bookmark; + +import java.util.Collections; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Set; + +import codechicken.nei.BookmarkPanel.BookmarkViewMode; +import codechicken.nei.recipe.Recipe.RecipeId; + +public class GroupingItem { + + public static class PointEntry { + + public int cursorRowIndex; + public int topRowIndex; + public int bottomRowIndex; + + public PointEntry(int clickSlotIndex, int topSlotIndex, int bottomSlotIndex) { + this.cursorRowIndex = clickSlotIndex; + this.topRowIndex = topSlotIndex; + this.bottomRowIndex = bottomSlotIndex; + } + } + + public BookmarkGrid grid; + + public int groupId = -1; + public PointEntry startPoint; + public PointEntry endPoint; + + public GroupingItem(BookmarkGrid grid, int groupId, int overRowIndex) { + this.groupId = groupId; + this.grid = grid; + setStartPoint(overRowIndex); + } + + public void setStartPoint(int overRowIndex) { + + if (this.grid.getRowItemIndex(overRowIndex, true) != Integer.MIN_VALUE) { + this.startPoint = new PointEntry( + overRowIndex + getRowShift(), + getTopRowIndex(overRowIndex), + getBottomRowIndex(overRowIndex)); + } else { + this.startPoint = new PointEntry(overRowIndex + getRowShift(), -1, -1); + } + + } + + public void setEndPoint(int overRowIndex) { + if (this.grid.getRowItemIndex(overRowIndex, true) != Integer.MIN_VALUE) { + this.endPoint = new PointEntry( + overRowIndex + getRowShift(), + getTopRowIndex(overRowIndex), + getBottomRowIndex(overRowIndex)); + } + } + + public int getStartSlotIndex() { + return this.startPoint.cursorRowIndex - getRowShift(); + } + + public int getEndSlotIndex() { + return this.endPoint != null ? this.endPoint.cursorRowIndex - getRowShift() : 0; + } + + public boolean hasEndRow() { + return this.endPoint != null; + } + + private int getRowShift() { + return (this.grid.getPage() - 1) * this.grid.getRows(); + } + + public Map prepareGroups(Map groups) { + final int topRowIndex = Math.min(this.startPoint.topRowIndex, this.endPoint.topRowIndex); + final int bottomRowIndex = Math.max(this.startPoint.bottomRowIndex, this.endPoint.bottomRowIndex); + groups = new HashMap<>(groups); + + if (this.groupId != BookmarkGrid.DEFAULT_GROUP_ID) { + + if (this.startPoint.cursorRowIndex > this.endPoint.cursorRowIndex) { + int rowIndex = this.endPoint.topRowIndex - 1; + + while (groups.containsKey(rowIndex) && groups.get(rowIndex) == this.groupId) { + groups.put(rowIndex--, BookmarkGrid.DEFAULT_GROUP_ID); + } + + } else if (this.startPoint.cursorRowIndex < this.endPoint.cursorRowIndex) { + int rowIndex = this.endPoint.bottomRowIndex + 1; + + while (groups.containsKey(rowIndex) && groups.get(rowIndex) == this.groupId) { + groups.put(rowIndex++, BookmarkGrid.DEFAULT_GROUP_ID); + } + } + + } else if (groups.containsKey(topRowIndex - 1) + && groups.get(topRowIndex - 1).equals(groups.get(bottomRowIndex + 1))) { + int groupId = groups.get(bottomRowIndex); + int rowIndex = bottomRowIndex + 1; + + while (groups.containsKey(rowIndex) && groups.get(rowIndex) == groupId) { + groups.put(rowIndex, -1 * groups.get(rowIndex)); + rowIndex++; + } + } + + for (int rowIndex = topRowIndex; rowIndex <= bottomRowIndex && groups.containsKey(rowIndex); rowIndex++) { + groups.put(rowIndex, this.groupId); + } + + return groups; + } + + protected int getTopRowIndex(int overRowIndex) { + final int rowShift = getRowShift(); + int itemIndex = this.grid.getRowItemIndex(overRowIndex, true); + + if (itemIndex == Integer.MIN_VALUE) { + return -1; + } + + final BookmarkItem item = this.grid.getCalculatedItem(itemIndex); + + if (item.recipeId != null) { + final List sortedItems = this.grid.getSortedItems(); + final BookmarkGroup group = this.grid.getGroup(item.groupId); + final boolean isCollapsedGroup = group.collapsed + || group.crafting != null && group.viewMode == BookmarkViewMode.DEFAULT; + final RecipeId recipeId = group.crafting != null + ? group.crafting.itemToRecipe.getOrDefault(itemIndex, item.recipeId) + : item.recipeId; + final Set recipeRelations = group.crafting != null + ? group.crafting.recipeRelations.getOrDefault(recipeId, Collections.singleton(recipeId)) + : Collections.singleton(recipeId); + + for (Integer rItemIndex : sortedItems) { + final BookmarkItem rItem = this.grid.getCalculatedItem(rItemIndex); + + if (rItem.groupId == item.groupId && (isCollapsedGroup || recipeRelations.contains(rItem.recipeId))) { + return this.grid.getAbsoluteSlotIndex(rItemIndex) / this.grid.getColumns(); + } + } + + return -1; + } + + return rowShift + overRowIndex; + } + + protected int getBottomRowIndex(int overRowIndex) { + final int rowShift = getRowShift(); + final int itemIndex = this.grid.getRowItemIndex(overRowIndex, false); + + if (itemIndex == Integer.MIN_VALUE) { + return -1; + } + + final BookmarkItem item = this.grid.getCalculatedItem(itemIndex); + + if (item.recipeId != null) { + final List sortedItems = this.grid.getSortedItems(); + final BookmarkGroup group = this.grid.getGroup(item.groupId); + final boolean isCollapsedGroup = group.collapsed + || group.crafting != null && group.viewMode == BookmarkViewMode.DEFAULT; + final RecipeId recipeId = group.crafting != null + ? group.crafting.itemToRecipe.getOrDefault(itemIndex, item.recipeId) + : item.recipeId; + final Set recipeRelations = group.crafting != null + ? group.crafting.recipeRelations.getOrDefault(recipeId, Collections.singleton(recipeId)) + : Collections.singleton(recipeId); + + for (int index = sortedItems.size() - 1; index >= 0; index--) { + final int rItemIndex = sortedItems.get(index); + final BookmarkItem rItem = this.grid.getCalculatedItem(rItemIndex); + + if (rItem.groupId == item.groupId && (isCollapsedGroup || recipeRelations.contains(rItem.recipeId))) { + return this.grid.getAbsoluteSlotIndex(rItemIndex) / this.grid.getColumns(); + } + } + + return -1; + } + + return rowShift + overRowIndex; + } + +} diff --git a/src/main/java/codechicken/nei/bookmark/RecipeChainDetails.java b/src/main/java/codechicken/nei/bookmark/RecipeChainDetails.java new file mode 100644 index 00000000..445ff145 --- /dev/null +++ b/src/main/java/codechicken/nei/bookmark/RecipeChainDetails.java @@ -0,0 +1,374 @@ +package codechicken.nei.bookmark; + +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.Map; +import java.util.Set; +import java.util.stream.Collectors; + +import net.minecraft.item.ItemStack; + +import codechicken.nei.recipe.Recipe.RecipeId; +import codechicken.nei.recipe.StackInfo; +import codechicken.nei.recipe.chain.RecipeChainMath; + +public class RecipeChainDetails { + + public enum CalculatedType { + + INGREDIENT, + RESULT, + REMAINDER; + + public int toInt() { + switch (this) { + case RESULT: + return 0; + case REMAINDER: + return 1; + case INGREDIENT: + return 2; + } + return -1; + } + } + + public static class BookmarkChainItem { + + private final BookmarkItem item; + + private long realAmount = 0L; + private long shiftAmount = 0L; + private long calculatedAmount = 0L; + + public CalculatedType calculatedType; + + protected BookmarkChainItem(BookmarkItem item, long shiftItem, long calculatedItem, + CalculatedType calculatedType) { + this.item = item.copy(); + this.shiftAmount = shiftItem; + this.calculatedAmount = calculatedItem; + this.calculatedType = calculatedType; + } + + public static BookmarkChainItem of(BookmarkItem item, long shiftItem, long calculatedAmount, + CalculatedType calculatedType) { + return new BookmarkChainItem(item, shiftItem, calculatedAmount, calculatedType); + } + + public static BookmarkChainItem of(BookmarkItem item, long shiftItem, CalculatedType calculatedType) { + return new BookmarkChainItem(item, shiftItem, item.amount, calculatedType); + } + + public static BookmarkChainItem of(BookmarkItem item) { + return new BookmarkChainItem( + item, + item.amount, + item.amount, + item.isIngredient ? CalculatedType.INGREDIENT : CalculatedType.RESULT); + } + + public ItemStack getItemStack(long amount) { + return this.item.getItemStack(amount); + } + + public void setRealAmount(long amount) { + this.realAmount = amount; + } + + public long getRealAmount() { + return this.realAmount; + } + + public long getShiftAmount() { + return this.shiftAmount; + } + + public long getCalculatedAmount() { + return this.calculatedAmount; + } + + public BookmarkItem getItem() { + return this.item; + } + + public void append(long amount, long calculatedAmount) { + this.shiftAmount += amount; + this.calculatedAmount += calculatedAmount; + this.item.amount = this.calculatedAmount; + } + } + + public final Map calculatedItems = new HashMap<>(); + public final Map itemToRecipe = new HashMap<>(); + public final Set outputRecipes = new HashSet<>(); + public final Set recipeInMiddle = new HashSet<>(); + + public final Map> recipeRelations = new HashMap<>(); + + public void refresh(Map chainItems, Set collapsedRecipes) { + final Map sortingChainItems = new HashMap<>(); + + for (Map.Entry entry : chainItems.entrySet()) { + sortingChainItems.put(entry.getValue(), entry.getKey()); + } + + this.itemToRecipe.clear(); + this.outputRecipes.clear(); + this.recipeInMiddle.clear(); + this.calculatedItems.clear(); + this.recipeRelations.clear(); + final RecipeChainMath math = RecipeChainMath.of(new ArrayList<>(chainItems.values()), collapsedRecipes) + .refresh(); + + this.outputRecipes.addAll(math.outputRecipes.keySet()); + + for (BookmarkItem item : math.preferredItems.values()) { + if (item != null && item.recipeId != null) { + this.recipeInMiddle.add(item.recipeId); + } + } + + for (BookmarkItem item : math.initialItems) { + this.calculatedItems.put( + sortingChainItems.get(item), + BookmarkChainItem.of(item, math.requiredAmount.getOrDefault(item, 0L), CalculatedType.INGREDIENT)); + } + + if (collapsedRecipes.isEmpty()) { + + for (BookmarkItem item : math.recipeResults) { + generateResult(math, item, sortingChainItems.get(item)); + } + + for (BookmarkItem item : math.recipeIngredients) { + generateIngredient(math, item, sortingChainItems.get(item)); + } + + } else { + final Set topLevelRecipes = findTopLevelRecipes(math, collapsedRecipes); + + for (RecipeId recipeId : collapsedRecipes) { + this.recipeRelations.put( + recipeId, + getRecipeRelations(math, recipeId, topLevelRecipes, new HashSet<>(Arrays.asList(recipeId)))); + generateCollapsedRecipe(math, recipeId, topLevelRecipes, sortingChainItems); + generateShadowItems(recipeId, chainItems.values(), sortingChainItems); + } + + for (BookmarkItem item : math.recipeResults) { + if (!collapsedRecipes.contains(item.recipeId) && topLevelRecipes.contains(item.recipeId)) { + generateResult(math, item, sortingChainItems.get(item)); + } + } + + for (BookmarkItem item : math.recipeIngredients) { + if (!collapsedRecipes.contains(item.recipeId) && topLevelRecipes.contains(item.recipeId)) { + generateIngredient(math, item, sortingChainItems.get(item)); + } + } + + } + + for (Map.Entry entry : chainItems.entrySet()) { + final int itemIndex = entry.getKey(); + if (itemIndex >= 0 && this.calculatedItems.containsKey(itemIndex)) { + final BookmarkItem item = entry.getValue(); + if (this.recipeInMiddle.contains(item.recipeId)) { + this.calculatedItems.get(itemIndex).setRealAmount(item.amount); + } else { + this.calculatedItems.get(itemIndex).setRealAmount(Math.max(item.factor, item.amount)); + } + } + } + + } + + private Set getRecipeRelations(RecipeChainMath math, RecipeId recipeId, Set topLevelRecipes, + Set recipes) { + + for (BookmarkItem item : math.recipeIngredients) { + if (item.recipeId.equals(recipeId)) { + final BookmarkItem prefItem = math.preferredItems.get(item); + if (prefItem != null && !topLevelRecipes.contains(prefItem.recipeId) + && recipes.add(prefItem.recipeId)) { + getRecipeRelations(math, prefItem.recipeId, topLevelRecipes, recipes); + } + } + } + + return recipes; + } + + private void generateCollapsedRecipe(RecipeChainMath math, RecipeId recipeId, Set topLevelRecipes, + Map sortingChainItems) { + + final List items = collectItems( + math, + recipeId, + this.recipeRelations.get(recipeId), + topLevelRecipes); + + for (BookmarkChainItem value : items) { + int itemIndex = sortingChainItems.get(value.getItem()); + + if (value.getItem().isIngredient || !recipeId.equals(value.getItem().recipeId)) { + itemIndex *= -1; + } + + if (!value.getItem().isIngredient && !math.outputRecipes.containsKey(value.getItem().recipeId)) { + value.calculatedType = CalculatedType.REMAINDER; + } + + this.itemToRecipe.put(itemIndex, recipeId); + this.calculatedItems.put(itemIndex, value); + } + } + + private void generateShadowItems(RecipeId recipeId, Iterable chainItems, + Map sortingChainItems) { + final Set recipeRelations = this.recipeRelations.get(recipeId); + final List subChainItems = new ArrayList<>(); + + for (BookmarkItem item : chainItems) { + if (recipeRelations.contains(item.recipeId)) { + subChainItems.add(item.copyWithAmount(recipeId.equals(item.recipeId) ? item.amount : 0)); + } + } + + final RecipeChainMath math = RecipeChainMath.of(subChainItems, Collections.emptySet()).refresh(); + final List items = collectItems( + math, + recipeId, + this.recipeRelations.get(recipeId), + Collections.emptySet()); + + for (BookmarkChainItem value : items) { + int itemIndex = sortingChainItems.get(value.getItem()); + + if (value.getItem().isIngredient || !recipeId.equals(value.getItem().recipeId)) { + itemIndex *= -1; + } + + if (this.calculatedItems.containsKey(itemIndex)) { + this.calculatedItems.get(itemIndex).setRealAmount(value.getShiftAmount()); + } + } + + } + + private List collectItems(RecipeChainMath math, RecipeId recipeId, Set recipeRelations, + Set topLevelRecipes) { + final Map items = new HashMap<>(); + final Map multiplier = new HashMap<>(); + + for (RecipeId relRecipeId : recipeRelations) { + for (BookmarkItem item : math.recipeResults) { + if (item.recipeId.equals(relRecipeId)) { + final long amount = item.amount - math.requiredAmount.getOrDefault(item, 0L); + + if (item.recipeId.equals(recipeId)) { + multiplier.put(item.recipeId, item.getMultiplier()); + items.computeIfAbsent( + StackInfo.getItemStackGUID(item.itemStack), + is -> BookmarkChainItem.of(item, 0, 0, CalculatedType.RESULT)) + .append(amount, item.amount); + } else if (amount > 0) { + items.computeIfAbsent( + StackInfo.getItemStackGUID(item.itemStack), + is -> BookmarkChainItem.of(item, 0, 0, CalculatedType.RESULT)).append(amount, amount); + } + } + } + + for (BookmarkItem item : math.recipeIngredients) { + if (item.recipeId.equals(relRecipeId)) { + final BookmarkItem prefItem = math.preferredItems.get(item); + final long amount = math.requiredAmount.containsKey(prefItem) ? 0 + : math.requiredAmount.getOrDefault(item, item.amount); + final long refAmount = prefItem != null && !topLevelRecipes.contains(prefItem.recipeId) + ? math.requiredAmount.getOrDefault(prefItem, 0L) + : 0; + + if (amount != 0 || item.amount > refAmount && math.requiredAmount.containsKey(prefItem) + || recipeId.equals(item.recipeId) && multiplier.get(recipeId) == 0) { + items.computeIfAbsent( + StackInfo.getItemStackGUID(item.itemStack), + is -> BookmarkChainItem.of(item, 0, 0, CalculatedType.INGREDIENT)) + .append(amount, item.amount - refAmount); + } + } + } + } + + return new ArrayList<>(items.values()); + } + + private void generateResult(RecipeChainMath math, BookmarkItem item, int itemIndex) { + final long amount = item.amount - math.requiredAmount.getOrDefault(item, 0L); + final CalculatedType calculatedType = math.outputRecipes.containsKey(item.recipeId) ? CalculatedType.RESULT + : CalculatedType.REMAINDER; + + this.calculatedItems.put(itemIndex, BookmarkChainItem.of(item, amount, calculatedType)); + } + + private void generateIngredient(RecipeChainMath math, BookmarkItem item, int itemIndex) { + final BookmarkItem prefItem = math.preferredItems.get(item); + final long amount = math.requiredAmount.containsKey(prefItem) ? 0 + : math.requiredAmount.getOrDefault(item, item.amount); + + this.calculatedItems.put(itemIndex, BookmarkChainItem.of(item, amount, CalculatedType.INGREDIENT)); + } + + private Set findTopLevelRecipes(RecipeChainMath math, Set collapsedRecipes) { + final Set result = new HashSet<>(math.outputRecipes.keySet()); + + for (BookmarkItem item : math.recipeResults) { + if (!result.contains(item.recipeId)) { + final Set parents = getRecipeParents( + math, + item.recipeId, + new HashSet<>(), + new HashSet<>(Arrays.asList(item.recipeId))); + + if (parents.size() != 1 || parents.stream().noneMatch(collapsedRecipes::contains)) { + result.add(item.recipeId); + } + } + } + + return result; + } + + private Set getRecipeParents(RecipeChainMath math, RecipeId recipeId, Set parents, + Set visited) { + final Set recipeIds = math.preferredItems.entrySet().stream() + .filter( + entry -> entry.getValue() != null && recipeId.equals(entry.getValue().recipeId) + && !visited.contains(entry.getKey().recipeId)) + .map(entry -> entry.getKey().recipeId).collect(Collectors.toSet()); + + visited.addAll(recipeIds); + + if (recipeIds.isEmpty()) { + parents.add(recipeId); + } else { + + for (RecipeId ingrRecipeId : recipeIds) { + if (math.outputRecipes.containsKey(ingrRecipeId)) { + parents.add(ingrRecipeId); + } else { + getRecipeParents(math, ingrRecipeId, parents, visited); + } + } + + } + + return parents; + } + +} diff --git a/src/main/java/codechicken/nei/bookmark/SortableGroup.java b/src/main/java/codechicken/nei/bookmark/SortableGroup.java new file mode 100644 index 00000000..51764313 --- /dev/null +++ b/src/main/java/codechicken/nei/bookmark/SortableGroup.java @@ -0,0 +1,83 @@ +package codechicken.nei.bookmark; + +import java.awt.Point; +import java.util.ArrayList; +import java.util.List; + +import org.lwjgl.opengl.GL11; + +import codechicken.lib.vec.Rectangle4i; +import codechicken.nei.guihook.GuiContainerManager; + +public class SortableGroup { + + public int groupId; + public BookmarkGrid grid; + + public int shiftX = 0; + public int shiftY = 0; + + public SortableGroup(BookmarkGrid grid, int groupId) { + this.grid = grid; + this.groupId = groupId; + } + + public void postDraw(int mousex, int mousey) { + Point startRect = null; + + GuiContainerManager.drawItems.zLevel += 100; + + for (BookmarksGridSlot slot : this.grid.getMask()) { + if (slot.getGroupId() == this.groupId) { + final Rectangle4i rect = this.grid.getSlotRect(slot.slotIndex); + + if (startRect == null) { + startRect = new Point(rect.x - this.shiftX, rect.y - this.shiftY); + } + + GuiContainerManager.drawItem( + mousex + rect.x - startRect.x + 1, + mousey + rect.y - startRect.y + 1, + slot.getItemStack(), + true, + ""); + + } + } + + if (startRect != null) { + int startRowIndex = -1; + int endRowIndex = 0; + + for (int rowIndex = 0; rowIndex < this.grid.getRows(); rowIndex++) { + if (this.grid.getRowGroupId(rowIndex) == this.groupId) { + + if (startRowIndex == -1) { + startRowIndex = rowIndex; + } + + endRowIndex = rowIndex; + } + } + + GL11.glPushMatrix(); + GL11.glTranslated(mousex - startRect.x, mousey - startRect.y, 0); + this.grid.drawGroup(this.groupId, startRowIndex, endRowIndex); + GL11.glPopMatrix(); + } + + GuiContainerManager.drawItems.zLevel -= 100; + } + + public List getBookmarkItems() { + final List items = new ArrayList<>(); + + for (BookmarkItem item : this.grid.bookmarkItems) { + if (item.groupId == this.groupId) { + items.add(item); + } + } + + return items; + } +} diff --git a/src/main/java/codechicken/nei/bookmark/SortableItem.java b/src/main/java/codechicken/nei/bookmark/SortableItem.java new file mode 100644 index 00000000..89030a98 --- /dev/null +++ b/src/main/java/codechicken/nei/bookmark/SortableItem.java @@ -0,0 +1,250 @@ +package codechicken.nei.bookmark; + +import java.awt.Point; +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; +import java.util.Map; + +import codechicken.lib.vec.Rectangle4i; +import codechicken.nei.BookmarkPanel.BookmarkViewMode; +import codechicken.nei.ItemsGrid; +import codechicken.nei.bookmark.RecipeChainDetails.BookmarkChainItem; +import codechicken.nei.guihook.GuiContainerManager; +import codechicken.nei.recipe.Recipe.RecipeId; + +public class SortableItem { + + public final BookmarkGrid grid; + public final BookmarkItem bookmarkItem; + public final boolean moveRecipe; + public final boolean crossGroup; + public final boolean isCollapsedRecipe; + public List items = new ArrayList<>(); + + public int shiftX = -8; + public int shiftY = -8; + + public SortableItem(BookmarkGrid grid, BookmarkItem bookmarkItem) { + final BookmarkGroup group = grid.getGroup(bookmarkItem.groupId); + + this.grid = grid; + this.bookmarkItem = bookmarkItem.copy(); + this.moveRecipe = bookmarkItem.recipeId != null && !bookmarkItem.isIngredient + && (group.viewMode == BookmarkViewMode.TODO_LIST || group.crafting != null); + this.crossGroup = !group.collapsed && group.viewMode == BookmarkViewMode.TODO_LIST + && !this.bookmarkItem.isIngredient; + this.isCollapsedRecipe = group.crafting != null && group.crafting.itemToRecipe.values().stream() + .anyMatch(recipeId -> this.bookmarkItem.equalsRecipe(recipeId, this.bookmarkItem.groupId)); + + this.update(); + } + + public void postDraw(int mousex, int mousey) { + Point startRect = null; + + GuiContainerManager.drawItems.zLevel += 100; + + for (Integer itemIndex : this.grid.getSortedItems()) { + if (this.items.contains(itemIndex)) { + final BookmarkItem item = this.grid.getCalculatedItem(itemIndex); + final int slotIndex = this.grid.gridGenerator.itemToSlot.get(itemIndex); + final Rectangle4i rect = this.grid + .getSlotRect(slotIndex % (this.grid.getRows() * this.grid.getColumns())); + + if (startRect == null) { + startRect = new Point(rect.x - this.shiftX, rect.y - this.shiftY); + } + + GuiContainerManager.drawItem( + mousex + rect.x - startRect.x + 1, + mousey + rect.y - startRect.y + 1, + item.getItemStack(), + true, + ""); + + } + } + + GuiContainerManager.drawItems.zLevel -= 100; + } + + public void update() { + final BookmarkGroup group = this.grid.getGroup(this.bookmarkItem.groupId); + + this.items.clear(); + + if (group.collapsed) { + return; + } + + for (Map.Entry entry : this.grid.gridGenerator.caclulatedItems.entrySet()) { + if (containsItem(entry.getValue().getItem())) { + this.items.add(entry.getKey()); + } + } + + if (this.isCollapsedRecipe && group.crafting != null) { + for (Map.Entry entry : group.crafting.itemToRecipe.entrySet()) { + if (this.bookmarkItem.equalsRecipe(entry.getValue(), this.bookmarkItem.groupId)) { + this.items.add(entry.getKey()); + } + } + } + + } + + public boolean isEmpty() { + return this.items.isEmpty(); + } + + public int getItemIndex() { + return this.items.isEmpty() ? -1 : this.items.get(0); + } + + private boolean canInsert(BookmarkGroup group) { + return group.viewMode == BookmarkViewMode.TODO_LIST && !group.collapsed; + } + + public void mouseDragged(int mousex, int mousey) { + final BookmarkViewMode sortViewMode = this.grid.getViewMode(this.bookmarkItem.groupId); + BookmarksGridSlot mouseOverSlot = this.grid.getSlotMouseOver(mousex, mousey); + + if (this.crossGroup) { + final Rectangle4i topLeftSlot = this.grid.getSlotRect(0); + mouseOverSlot = this.grid.getSlotMouseOver(topLeftSlot.x, mousey); + + if (mouseOverSlot != null) { + float ySlot = (float) (mousey - topLeftSlot.y) / ItemsGrid.SLOT_SIZE; + int overRowIndex = (int) ySlot; + int beforeGroupId = this.grid.getRowGroupId(overRowIndex - 1); + int afterGroupId = this.grid.getRowGroupId(overRowIndex + 1); + int overGroupId = mouseOverSlot.getGroupId(); + ySlot -= overRowIndex; + + if (overGroupId == this.bookmarkItem.groupId && !this.items.contains(mouseOverSlot.itemIndex)) { + this.grid.moveItem(this, mouseOverSlot.itemIndex); + } else if (ySlot <= 0.25) { + final BookmarkGroup group = this.grid.getGroup(beforeGroupId); + + if (beforeGroupId != overGroupId && canInsert(group) + && !existsRecipe(this.bookmarkItem.recipeId, beforeGroupId)) { + this.grid.moveItem(this, overRowIndex, beforeGroupId, false); + } + + } else if (ySlot > 0.25 && ySlot <= 0.5) { + + if (beforeGroupId == overGroupId) { + return; + } + + if (overGroupId != BookmarkGrid.DEFAULT_GROUP_ID + && beforeGroupId != BookmarkGrid.DEFAULT_GROUP_ID) { + beforeGroupId = BookmarkGrid.DEFAULT_GROUP_ID; + } + + if (overGroupId == BookmarkGrid.DEFAULT_GROUP_ID && beforeGroupId != BookmarkGrid.DEFAULT_GROUP_ID + && afterGroupId != BookmarkGrid.DEFAULT_GROUP_ID) { + return; + } + + if (canInsert(this.grid.getGroup(beforeGroupId)) + && !existsRecipe(this.bookmarkItem.recipeId, beforeGroupId)) { + this.grid.moveItem(this, overRowIndex, beforeGroupId, false); + } + + } else if (ySlot > 0.5 && ySlot < 0.75) { + + if (afterGroupId == overGroupId) { + return; + } + + if (overGroupId != BookmarkGrid.DEFAULT_GROUP_ID && afterGroupId != BookmarkGrid.DEFAULT_GROUP_ID) { + afterGroupId = BookmarkGrid.DEFAULT_GROUP_ID; + } + + if (overGroupId == BookmarkGrid.DEFAULT_GROUP_ID && beforeGroupId != BookmarkGrid.DEFAULT_GROUP_ID + && afterGroupId != BookmarkGrid.DEFAULT_GROUP_ID) { + return; + } + + if (canInsert(this.grid.getGroup(afterGroupId)) + && !existsRecipe(this.bookmarkItem.recipeId, afterGroupId)) { + this.grid.moveItem(this, overRowIndex, afterGroupId, true); + } + + } else if (ySlot >= 0.75) { + final BookmarkGroup group = this.grid.getGroup(afterGroupId); + + if (afterGroupId != overGroupId && canInsert(group) + && !existsRecipe(this.bookmarkItem.recipeId, afterGroupId)) { + this.grid.moveItem(this, overRowIndex, afterGroupId, true); + } + + } + + } else { + final int overRowIndex = this.grid.getHoveredRowIndex(false); + final int overGroupId = this.grid.getRowGroupId(overRowIndex); + + if (overRowIndex != -1 && overGroupId == this.bookmarkItem.groupId) { + final int absoluteSlotIndex = ((this.grid.getPage() - 1) * this.grid.getRows() + overRowIndex) + * this.grid.getColumns(); + + if (absoluteSlotIndex < this.grid.gridGenerator.itemToSlot.getOrDefault(getItemIndex(), -1)) { + this.grid.moveItem(this, overRowIndex, overGroupId, true); + } else { + this.grid.moveItem(this, overRowIndex, overGroupId, false); + } + + } + + } + + } else if (mouseOverSlot != null && !this.items.contains(mouseOverSlot.itemIndex) + && mouseOverSlot.getGroupId() == this.bookmarkItem.groupId + && (sortViewMode == BookmarkViewMode.DEFAULT || sortViewMode == BookmarkViewMode.TODO_LIST + && mouseOverSlot.getBookmarkItem().equalsRecipe(this.bookmarkItem))) { + this.grid.moveItem(this, mouseOverSlot.itemIndex); + } + + } + + private boolean existsRecipe(RecipeId recipeId, int groupId) { + if (recipeId == null) return false; + + for (int itemIndex = 0; itemIndex < this.grid.bookmarkItems.size(); itemIndex++) { + if (!this.items.contains(itemIndex) + && this.grid.bookmarkItems.get(itemIndex).equalsRecipe(recipeId, groupId)) { + return true; + } + } + + return false; + } + + public List getBookmarkItems() { + final BookmarkGroup group = this.grid.getGroup(this.bookmarkItem.groupId); + final List items = new ArrayList<>(); + final List recipeRelations = new ArrayList<>(); + + if (this.isCollapsedRecipe && group.crafting != null) { + recipeRelations.addAll( + group.crafting.recipeRelations.getOrDefault(this.bookmarkItem.recipeId, Collections.emptySet())); + } + + for (BookmarkItem item : this.grid.bookmarkItems) { + if (containsItem(item) + || item.groupId == this.bookmarkItem.groupId && recipeRelations.contains(item.recipeId)) { + items.add(item); + } + } + + return items; + } + + public boolean containsItem(BookmarkItem slotItem) { + return this.moveRecipe ? this.bookmarkItem.equalsRecipe(slotItem) : this.bookmarkItem.equals(slotItem); + } + +} diff --git a/src/main/java/codechicken/nei/config/GuiPanelSettings.java b/src/main/java/codechicken/nei/config/GuiPanelSettings.java index 3f11d5b5..ec2a4618 100644 --- a/src/main/java/codechicken/nei/config/GuiPanelSettings.java +++ b/src/main/java/codechicken/nei/config/GuiPanelSettings.java @@ -1,9 +1,7 @@ package codechicken.nei.config; -import static codechicken.lib.gui.GuiDraw.getMousePosition; - import java.awt.Point; -import java.util.ArrayList; +import java.util.List; import net.minecraft.client.Minecraft; import net.minecraft.client.renderer.RenderHelper; @@ -14,6 +12,7 @@ import org.lwjgl.opengl.GL11; import org.lwjgl.opengl.GL12; import codechicken.core.gui.GuiScreenWidget; +import codechicken.lib.gui.GuiDraw; import codechicken.lib.render.CCRenderState; import codechicken.lib.vec.Rectangle4i; import codechicken.nei.ItemPanels; @@ -114,8 +113,8 @@ public class GuiPanelSettings extends GuiScreenWidget { } protected int[] getPaddings() { - final int minWidth = 5 * ItemsGrid.SLOT_SIZE; - final int minHeight = 9 * ItemsGrid.SLOT_SIZE; + final int minWidth = ItemsGrid.SLOT_SIZE; + final int minHeight = ItemsGrid.SLOT_SIZE; int paddingLeft = (int) Math.ceil(margin.w * opt.renderTag(name + ".left").getIntValue() / 100000.0); int paddingTop = (int) Math.ceil(margin.h * opt.renderTag(name + ".top").getIntValue() / 100000.0); @@ -123,7 +122,7 @@ public class GuiPanelSettings extends GuiScreenWidget { int paddingBottom = (int) Math.ceil(margin.h * opt.renderTag(name + ".bottom").getIntValue() / 100000.0); if (dragDir != null) { - final Point mouse = getMousePosition(); + final Point mouse = GuiDraw.getMousePosition(); Point drag = new Point(mouse.x - dragDown.x, mouse.y - dragDown.y); if (dragDir == "left") { @@ -164,13 +163,12 @@ public class GuiPanelSettings extends GuiScreenWidget { final int columns = w / ItemsGrid.SLOT_SIZE; final int rows = (h - (PANEL_SIZE + 2) * 2) / ItemsGrid.SLOT_SIZE; final int paddingLeft = (w % ItemsGrid.SLOT_SIZE) / 2; - final ArrayList items = ItemPanels.itemPanel.getItems(); + final List items = ItemPanels.itemPanel.getItems(); if (items.isEmpty()) return; for (int r = 0; r < rows; r++) { for (int c = 0; c < columns; c++) { - - GuiContainerManager.drawItem( + drawItem( x + paddingLeft + ItemsGrid.SLOT_SIZE * c + 1, y + PANEL_SIZE + 2 + ItemsGrid.SLOT_SIZE * r + 1, items.get((r * columns + c) % items.size())); @@ -178,6 +176,21 @@ public class GuiPanelSettings extends GuiScreenWidget { } } + protected void drawItem(int x, int y, ItemStack stack) { + final float scale = ItemsGrid.SLOT_SIZE / 18f; + final float inverseScaleFactor = 1.0f / scale; + final float shiftX = x * inverseScaleFactor; + final float shiftY = y * inverseScaleFactor; + + GL11.glScaled(scale, scale, 1); + GL11.glTranslated(shiftX, shiftY, 0); + + GuiContainerManager.drawItem(0, 0, stack, true, ""); + + GL11.glTranslated(-1 * shiftX, -1 * shiftY, 0); + GL11.glScaled(inverseScaleFactor, inverseScaleFactor, 1); + } + public boolean contains(int pointX, int pointY) { return (new Rectangle4i(x, y, w, h)).contains(pointX, pointY); } @@ -185,7 +198,7 @@ public class GuiPanelSettings extends GuiScreenWidget { protected void mouseClicked(int x, int y, int button) { if (contains(x, y)) { - dragDown = getMousePosition(); + dragDown = GuiDraw.getMousePosition(); if (leftButton.contains(x, y)) { dragDir = "left"; diff --git a/src/main/java/codechicken/nei/config/preset/LeftPanel.java b/src/main/java/codechicken/nei/config/preset/LeftPanel.java index 01518868..830569d6 100644 --- a/src/main/java/codechicken/nei/config/preset/LeftPanel.java +++ b/src/main/java/codechicken/nei/config/preset/LeftPanel.java @@ -13,8 +13,8 @@ import org.lwjgl.opengl.GL12; import codechicken.core.gui.GuiWidget; import codechicken.lib.gui.GuiDraw; import codechicken.lib.vec.Rectangle4i; -import codechicken.nei.ItemPanel.ItemPanelSlot; import codechicken.nei.ItemsGrid; +import codechicken.nei.ItemsGrid.ItemsGridSlot; import codechicken.nei.Label; import codechicken.nei.NEIClientUtils; import codechicken.nei.PresetsList.Preset; @@ -49,7 +49,7 @@ public class LeftPanel extends GuiWidget { @Override protected ItemFilter getFilter() { - Set identifiers = preset.items; + final Set identifiers = preset.items; return item -> identifiers.contains(Preset.getIdentifier(item)); } @@ -57,6 +57,20 @@ public class LeftPanel extends GuiWidget { protected boolean isSelected(ItemStack stack) { return mouseSelection != null && mouseSelection.items.contains(stack); } + + @Override + protected MouseContext getMouseContext(int mousex, int mousey) { + final ItemsGridSlot hovered = getSlotMouseOver(mousex, mousey); + + if (hovered != null) { + return new MouseContext( + hovered.slotIndex, + hovered.slotIndex / this.columns, + hovered.slotIndex % this.columns); + } + + return null; + } }; protected Label nameLabel = new Label(NEIClientUtils.translate("presets.name"), false); @@ -169,10 +183,10 @@ public class LeftPanel extends GuiWidget { } if (mouseSelection == null && button == 0) { - ItemPanelSlot slot = grid.getSlotMouseOver(x, y); + final ItemsGridSlot slot = grid.getSlotMouseOver(x, y); if (slot != null) { - mouseSelection = new MouseSelection(slot.slotIndex, grid.getItemRect(slot.slotIndex)); + mouseSelection = new MouseSelection(slot.itemIndex, grid.getItemRect(slot.itemIndex)); } } @@ -185,10 +199,10 @@ public class LeftPanel extends GuiWidget { nameField.mouseUp(x, y, button); if (mouseSelection != null && button == 0) { - ItemPanelSlot hoverSlot = grid.getSlotMouseOver(x, y); + final ItemsGridSlot hoverSlot = grid.getSlotMouseOver(x, y); - if (hoverSlot != null && hoverSlot.slotIndex == mouseSelection.startIndex) { - preset.items.remove(Preset.getIdentifier(hoverSlot.item)); + if (hoverSlot != null && hoverSlot.itemIndex == mouseSelection.startIndex) { + preset.items.remove(Preset.getIdentifier(hoverSlot.getItemStack())); } else if (!mouseSelection.items.isEmpty()) { for (ItemStack stack : mouseSelection.items) { @@ -206,13 +220,13 @@ public class LeftPanel extends GuiWidget { nameField.mouseDragged(x, y, button, time); if (mouseSelection != null && button == 0) { - final ItemPanelSlot slot = grid.getSlotMouseOver(x, y); + final ItemsGridSlot slot = grid.getSlotMouseOver(x, y); - if (slot != null && slot.slotIndex != mouseSelection.endIndex) { - mouseSelection.endIndex = slot.slotIndex; + if (slot != null && slot.itemIndex != mouseSelection.endIndex) { + mouseSelection.endIndex = slot.itemIndex; mouseSelection.items.clear(); - final Rectangle4i rec = grid.getItemRect(slot.slotIndex); + final Rectangle4i rec = grid.getItemRect(slot.itemIndex); final Rectangle4i sel = new Rectangle4i( Math.min(rec.x, mouseSelection.startX), Math.min(rec.y, mouseSelection.startY), @@ -221,10 +235,10 @@ public class LeftPanel extends GuiWidget { for (int ix = sel.x; ix <= sel.x + sel.w; ix += ItemsGrid.SLOT_SIZE) { for (int iy = sel.y; iy <= sel.y + sel.h; iy += ItemsGrid.SLOT_SIZE) { - ItemPanelSlot over = grid.getSlotMouseOver(ix, iy); + final ItemsGridSlot over = grid.getSlotMouseOver(ix, iy); if (over != null) { - mouseSelection.items.add(over.item); + mouseSelection.items.add(over.getItemStack()); } } } @@ -295,14 +309,14 @@ public class LeftPanel extends GuiWidget { } public List handleTooltip(int mousex, int mousey, List tooltip) { - ItemPanelSlot over = grid.getSlotMouseOver(mousex, mousey); + final ItemsGridSlot over = grid.getSlotMouseOver(mousex, mousey); if (over != null) { - tooltip = GuiContainerManager.itemDisplayNameMultiline(over.item, null, true); + tooltip = GuiContainerManager.itemDisplayNameMultiline(over.getItemStack(), null, true); synchronized (GuiContainerManager.tooltipHandlers) { for (IContainerTooltipHandler handler : GuiContainerManager.tooltipHandlers) { - tooltip = handler.handleItemTooltip(null, over.item, mousex, mousey, tooltip); + tooltip = handler.handleItemTooltip(null, over.getItemStack(), mousex, mousey, tooltip); } } diff --git a/src/main/java/codechicken/nei/config/preset/PresetItemsGrid.java b/src/main/java/codechicken/nei/config/preset/PresetItemsGrid.java index a65e5628..41dbfd16 100644 --- a/src/main/java/codechicken/nei/config/preset/PresetItemsGrid.java +++ b/src/main/java/codechicken/nei/config/preset/PresetItemsGrid.java @@ -5,31 +5,45 @@ import java.util.List; import java.util.concurrent.ExecutionException; import java.util.stream.Collectors; -import javax.annotation.Nullable; - import net.minecraft.client.gui.inventory.GuiContainer; import net.minecraft.item.ItemStack; -import codechicken.lib.gui.GuiDraw; import codechicken.lib.vec.Rectangle4i; import codechicken.nei.Button; import codechicken.nei.ItemList; -import codechicken.nei.ItemPanel.ItemPanelSlot; import codechicken.nei.ItemSorter; import codechicken.nei.ItemsGrid; +import codechicken.nei.ItemsGrid.ItemsGridSlot; import codechicken.nei.Label; +import codechicken.nei.NEIClientUtils; import codechicken.nei.RestartableTask; import codechicken.nei.api.ItemFilter; -public abstract class PresetItemsGrid extends ItemsGrid { +public abstract class PresetItemsGrid extends ItemsGrid { protected static final int BUTTON_SIZE = 16; + public class PresetsGridSlot extends ItemsGridSlot { + + public PresetsGridSlot(int slotIndex, int itemIndex, ItemStack itemStack) { + super(slotIndex, itemIndex, itemStack); + } + + @Override + public void beforeDraw(Rectangle4i rect, MouseContext mouseContext) { + if (mouseContext != null && mouseContext.slotIndex == this.slotIndex || isSelected(getItemStack())) { + NEIClientUtils.drawRect(rect.x, rect.y, rect.w, rect.h, HIGHLIGHT_COLOR); + } + } + + } + public Button pagePrev; public Label pageLabel; public Button pageNext; protected ArrayList newItems; + protected List gridMask; protected final RestartableTask updateFilter = new RestartableTask("NEI Presets Item Filtering") { @@ -139,6 +153,28 @@ public abstract class PresetItemsGrid extends ItemsGrid { this.newItems = new ArrayList<>(newItems); } + @Override + protected void onGridChanged() { + this.gridMask = null; + super.onGridChanged(); + } + + @Override + public List getMask() { + + if (this.gridMask == null) { + this.gridMask = new ArrayList<>(); + int itemIndex = page * perPage; + + for (int slotIndex = 0; slotIndex < rows * columns && itemIndex < size(); slotIndex++) { + this.gridMask.add(new PresetsGridSlot(slotIndex, itemIndex, getItem(itemIndex))); + itemIndex++; + } + } + + return this.gridMask; + } + @Override public void refresh(GuiContainer gui) { @@ -151,15 +187,6 @@ public abstract class PresetItemsGrid extends ItemsGrid { super.refresh(gui); } - @Override - protected void beforeDrawSlot(@Nullable ItemPanelSlot focused, int slotIdx, Rectangle4i rect) { - if (isSelected(getItem(slotIdx))) { - GuiDraw.drawRect(rect.x, rect.y, rect.w, rect.h, 0xee555555); - } else { - super.beforeDrawSlot(focused, slotIdx, rect); - } - } - protected abstract boolean isSelected(ItemStack stack); protected abstract ItemFilter getFilter(); diff --git a/src/main/java/codechicken/nei/config/preset/RightPanel.java b/src/main/java/codechicken/nei/config/preset/RightPanel.java index 1cc01db4..8f52c200 100644 --- a/src/main/java/codechicken/nei/config/preset/RightPanel.java +++ b/src/main/java/codechicken/nei/config/preset/RightPanel.java @@ -19,8 +19,8 @@ import codechicken.nei.CollapsibleItems; import codechicken.nei.ItemList.AllMultiItemFilter; import codechicken.nei.ItemList.AnyMultiItemFilter; import codechicken.nei.ItemList.NegatedItemFilter; -import codechicken.nei.ItemPanel.ItemPanelSlot; import codechicken.nei.ItemsGrid; +import codechicken.nei.ItemsGrid.ItemsGridSlot; import codechicken.nei.NEIClientConfig; import codechicken.nei.NEIClientUtils; import codechicken.nei.PresetsList; @@ -133,12 +133,12 @@ public class RightPanel extends GuiWidget { @Override protected ItemFilter getFilter() { - AllMultiItemFilter filter = new AllMultiItemFilter(searchField.getFilter()); + final AllMultiItemFilter filter = new AllMultiItemFilter(searchField.getFilter()); if (enabledPresets.isChecked()) { - AllMultiItemFilter andFilter = new AllMultiItemFilter(); - AnyMultiItemFilter orFilter = new AnyMultiItemFilter(); - Set identifiers = PresetsList.presets.stream().flatMap(p -> p.items.stream()) + final AllMultiItemFilter andFilter = new AllMultiItemFilter(); + final AnyMultiItemFilter orFilter = new AnyMultiItemFilter(); + final Set identifiers = PresetsList.presets.stream().flatMap(p -> p.items.stream()) .collect(Collectors.toSet()); andFilter.filters.add(item -> !identifiers.contains(Preset.getIdentifier(item))); @@ -164,6 +164,20 @@ public class RightPanel extends GuiWidget { return preset.items.contains(Preset.getIdentifier(stack)); } + + @Override + protected MouseContext getMouseContext(int mousex, int mousey) { + final ItemsGridSlot hovered = getSlotMouseOver(mousex, mousey); + + if (hovered != null) { + return new MouseContext( + hovered.slotIndex, + hovered.slotIndex / this.columns, + hovered.slotIndex % this.columns); + } + + return null; + } }; protected CheckboxButton enabledPresets; @@ -218,10 +232,10 @@ public class RightPanel extends GuiWidget { } if (mouseSelection == null && (button == 0 || button == 1)) { - ItemPanelSlot slot = grid.getSlotMouseOver(x, y); + final ItemsGridSlot slot = grid.getSlotMouseOver(x, y); if (slot != null) { - mouseSelection = new MouseSelection(slot.slotIndex, grid.getItemRect(slot.slotIndex), button == 0); + mouseSelection = new MouseSelection(slot.itemIndex, grid.getItemRect(slot.itemIndex), button == 0); } } @@ -234,10 +248,10 @@ public class RightPanel extends GuiWidget { searchField.mouseUp(x, y, button); if (mouseSelection != null && (button == 0 || button == 1)) { - ItemPanelSlot hoverSlot = grid.getSlotMouseOver(x, y); + final ItemsGridSlot hoverSlot = grid.getSlotMouseOver(x, y); - if (hoverSlot != null && hoverSlot.slotIndex == mouseSelection.startIndex) { - setHidden(hoverSlot.item, button == 0); + if (hoverSlot != null && hoverSlot.itemIndex == mouseSelection.startIndex) { + setHidden(hoverSlot.getItemStack(), button == 0); } else if (!mouseSelection.items.isEmpty()) { for (ItemStack stack : mouseSelection.items) { @@ -304,13 +318,13 @@ public class RightPanel extends GuiWidget { searchField.mouseDragged(x, y, button, time); if (mouseSelection != null && (button == 0 || button == 1)) { - final ItemPanelSlot slot = grid.getSlotMouseOver(x, y); + final ItemsGridSlot slot = grid.getSlotMouseOver(x, y); - if (slot != null && slot.slotIndex != mouseSelection.endIndex) { - mouseSelection.endIndex = slot.slotIndex; + if (slot != null && slot.itemIndex != mouseSelection.endIndex) { + mouseSelection.endIndex = slot.itemIndex; mouseSelection.items.clear(); - final Rectangle4i rec = grid.getItemRect(slot.slotIndex); + final Rectangle4i rec = grid.getItemRect(slot.itemIndex); final Rectangle4i sel = new Rectangle4i( Math.min(rec.x, mouseSelection.startX), Math.min(rec.y, mouseSelection.startY), @@ -319,10 +333,10 @@ public class RightPanel extends GuiWidget { for (int ix = sel.x; ix <= sel.x + sel.w; ix += ItemsGrid.SLOT_SIZE) { for (int iy = sel.y; iy <= sel.y + sel.h; iy += ItemsGrid.SLOT_SIZE) { - ItemPanelSlot over = grid.getSlotMouseOver(ix, iy); + final ItemsGridSlot over = grid.getSlotMouseOver(ix, iy); if (over != null) { - mouseSelection.items.add(over.item); + mouseSelection.items.add(over.getItemStack()); } } } @@ -363,14 +377,14 @@ public class RightPanel extends GuiWidget { } public List handleTooltip(int mousex, int mousey, List tooltip) { - ItemPanelSlot over = grid.getSlotMouseOver(mousex, mousey); + final ItemsGridSlot over = grid.getSlotMouseOver(mousex, mousey); if (over != null) { - tooltip = GuiContainerManager.itemDisplayNameMultiline(over.item, null, true); + tooltip = GuiContainerManager.itemDisplayNameMultiline(over.getItemStack(), null, true); synchronized (GuiContainerManager.tooltipHandlers) { for (IContainerTooltipHandler handler : GuiContainerManager.tooltipHandlers) { - tooltip = handler.handleItemTooltip(null, over.item, mousex, mousey, tooltip); + tooltip = handler.handleItemTooltip(null, over.getItemStack(), mousex, mousey, tooltip); } } diff --git a/src/main/java/codechicken/nei/guihook/GuiContainerManager.java b/src/main/java/codechicken/nei/guihook/GuiContainerManager.java index 6b29a5f8..3f2d6104 100644 --- a/src/main/java/codechicken/nei/guihook/GuiContainerManager.java +++ b/src/main/java/codechicken/nei/guihook/GuiContainerManager.java @@ -10,7 +10,6 @@ import java.awt.Point; import java.util.ArrayList; import java.util.Collections; import java.util.HashMap; -import java.util.HashSet; import java.util.LinkedList; import java.util.List; import java.util.Map; @@ -213,7 +212,7 @@ public class GuiContainerManager { @Nullable public static String itemCountDetails(ItemStack stack) { - FluidStack fluid = StackInfo.getFluid(stack); + final FluidStack fluid = StackInfo.getFluid(stack); if (fluid != null) { return fluidAmountDetails(fluid.amount * Math.max(1, stack.stackSize)); } else { @@ -294,7 +293,7 @@ public class GuiContainerManager { } private static int modelviewDepth = -1; - private static final HashSet stackTraces = new HashSet<>(); + private static boolean contextEnabled = false; private static final ItemStackSet renderingErrorItems = new ItemStackSet(); public static void drawItem(int offsetX, int offsetY, ItemStack itemstack, FontRenderer fontRenderer, @@ -350,30 +349,32 @@ public class GuiContainerManager { private static void safeItemRenderContext(ItemStack stack, int x, int y, FontRenderer fontRenderer, Runnable callback) { float zLevel = drawItems.zLevel += 100F; - GL11.glPushAttrib(GL11.GL_ALL_ATTRIB_BITS); + enableMatrixStackLogging(); enable3DRender(); - if (!renderingErrorItems.contains(stack)) { - try { - callback.run(); - - if (!checkMatrixStack()) throw new IllegalStateException("Modelview matrix stack too deep"); - if (Tessellator.instance.isDrawing) throw new IllegalStateException("Still drawing"); - } catch (Exception e) { - NEIClientUtils.reportErrorBuffered(e, stackTraces, stack); - - restoreMatrixStack(); - if (Tessellator.instance.isDrawing) Tessellator.instance.draw(); - - drawItems.zLevel = zLevel; + try { + if (renderingErrorItems.contains(stack)) { drawItems.renderItemIntoGUI(fontRenderer, renderEngine, new ItemStack(Blocks.fire), x, y); - renderingErrorItems.add(stack); + } else { + callback.run(); } - } else { + + if (!checkMatrixStack()) throw new IllegalStateException("Modelview matrix stack too deep"); + if (Tessellator.instance.isDrawing) throw new IllegalStateException("Still drawing"); + } catch (Exception e) { + System.err.println("Error while rendering: " + stack + " (" + e.getMessage() + ")"); + e.printStackTrace(); + + restoreMatrixStack(); + if (Tessellator.instance.isDrawing) Tessellator.instance.draw(); + + drawItems.zLevel = zLevel; drawItems.renderItemIntoGUI(fontRenderer, renderEngine, new ItemStack(Blocks.fire), x, y); + renderingErrorItems.add(stack); } - GL11.glPopAttrib(); + enable2DRender(); + disableMatrixStackLogging(); drawItems.zLevel = zLevel - 100; } @@ -384,11 +385,19 @@ public class GuiContainerManager { } public static void enableMatrixStackLogging() { - modelviewDepth = GL11.glGetInteger(GL11.GL_MODELVIEW_STACK_DEPTH); + if (!contextEnabled) { + GL11.glPushAttrib(GL11.GL_ALL_ATTRIB_BITS); + modelviewDepth = GL11.glGetInteger(GL11.GL_MODELVIEW_STACK_DEPTH); + contextEnabled = true; + } } public static void disableMatrixStackLogging() { - modelviewDepth = -1; + if (contextEnabled) { + contextEnabled = false; + modelviewDepth = -1; + GL11.glPopAttrib(); + } } public static boolean checkMatrixStack() { diff --git a/src/main/java/codechicken/nei/recipe/AcceptsFollowingTooltipLineHandler.java b/src/main/java/codechicken/nei/recipe/AcceptsFollowingTooltipLineHandler.java new file mode 100644 index 00000000..24232d36 --- /dev/null +++ b/src/main/java/codechicken/nei/recipe/AcceptsFollowingTooltipLineHandler.java @@ -0,0 +1,50 @@ +package codechicken.nei.recipe; + +import java.util.Comparator; +import java.util.List; + +import net.minecraft.item.ItemStack; + +import codechicken.nei.FavoriteRecipes; +import codechicken.nei.ItemsTooltipLineHandler; +import codechicken.nei.NEIClientUtils; + +public class AcceptsFollowingTooltipLineHandler extends ItemsTooltipLineHandler { + + protected static final int DEFAULT_MAX_ROWS = 5; + + public Object tooltipGUID; + + public AcceptsFollowingTooltipLineHandler(Object tooltipGUID, List items, ItemStack activeStack, + int maxRows) { + super(NEIClientUtils.translate("recipe.accepts"), items, false, maxRows); + this.tooltipGUID = tooltipGUID; + setActiveStack(activeStack); + } + + public static AcceptsFollowingTooltipLineHandler of(Object tooltipGUID, List items, + ItemStack activeStack) { + return of(tooltipGUID, items, activeStack, DEFAULT_MAX_ROWS); + } + + public static AcceptsFollowingTooltipLineHandler of(Object tooltipGUID, List items, + ItemStack activeStack, int maxRows) { + + if (items.size() > 1) { + items.sort(Comparator.comparing(FavoriteRecipes::contains).reversed()); + return new AcceptsFollowingTooltipLineHandler(tooltipGUID, items, activeStack, maxRows); + } + + return null; + } + + @Override + protected void drawItem(int x, int y, ItemStack drawStack, String stackSize) { + super.drawItem(x, y, drawStack, stackSize); + + if (FavoriteRecipes.contains(drawStack)) { + NEIClientUtils.drawNEIOverlayText("F", x, y); + } + } + +} diff --git a/src/main/java/codechicken/nei/recipe/AutoCraftingManager.java b/src/main/java/codechicken/nei/recipe/AutoCraftingManager.java new file mode 100644 index 00000000..47c0af83 --- /dev/null +++ b/src/main/java/codechicken/nei/recipe/AutoCraftingManager.java @@ -0,0 +1,150 @@ +package codechicken.nei.recipe; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collections; +import java.util.List; +import java.util.Map; + +import net.minecraft.client.gui.inventory.GuiContainer; +import net.minecraft.entity.player.InventoryPlayer; +import net.minecraft.init.Blocks; +import net.minecraft.item.ItemStack; + +import codechicken.nei.ItemStackAmount; +import codechicken.nei.NEIClientUtils; +import codechicken.nei.RestartableTask; +import codechicken.nei.bookmark.BookmarkItem; +import codechicken.nei.recipe.Recipe.RecipeId; +import codechicken.nei.recipe.chain.RecipeChainIterator; +import codechicken.nei.recipe.chain.RecipeChainMath; + +public class AutoCraftingManager { + + private static RecipeChainMath math; + + private static final RestartableTask task = new RestartableTask("NEI Bookmark AutoCraft Processing") { + + private static final ItemStack ROOT_ITEM = new ItemStack(Blocks.fire); + private static final RecipeId ROOT_RECIPE_ID = RecipeId + .of(ROOT_ITEM, "recipe-autocrafting", Collections.emptyList()); + + @Override + public void execute() { + final GuiContainer guiContainer = NEIClientUtils.getGuiContainer(); + final InventoryPlayer playerInventory = guiContainer.mc.thePlayer.inventory; + final ItemStackAmount inventory = ItemStackAmount.of(Arrays.asList(playerInventory.mainInventory)); + final RecipeChainMath math = createMasterRoot(AutoCraftingManager.math); + final List initialItems = prepareInitialItems(math, inventory); + boolean processed = false; + boolean changed = false; + + do { + changed = false; + + RecipeChainIterator iterator = new RecipeChainIterator(math, initialItems); + iterator.updateInventory(playerInventory.mainInventory); + + while (iterator.hasNext() && !interrupted(guiContainer)) { + final Map recipes = iterator.next(); + boolean craft = false; + + for (Map.Entry entry : recipes.entrySet()) { + final RecipeHandlerRef handler = RecipeHandlerRef.of(entry.getKey()); + + if (handler != null && handler.canCraft(guiContainer)) { + long multiplier = entry.getValue(); + + while (multiplier > 0 && !interrupted(guiContainer) + && handler.craft(guiContainer, (int) Math.min(64, multiplier))) { + multiplier -= 64; + } + + craft = multiplier != entry.getValue(); + } + + if (interrupted(guiContainer)) break; + } + + if (craft) { + changed = true; + processed = true; + iterator.updateInventory(playerInventory.mainInventory); + } + } + + } while (changed && !interrupted(guiContainer)); + + if (processed && !changed && !interrupted(guiContainer)) { + NEIClientUtils.playClickSound(); + } + + } + + @Override + public void clearTasks() { + super.clearTasks(); + AutoCraftingManager.math = null; + } + + private boolean interrupted(GuiContainer guiContainer) { + return interrupted() || guiContainer != NEIClientUtils.getGuiContainer(); + } + + private RecipeChainMath createMasterRoot(RecipeChainMath math) { + final List rootIngredients = new ArrayList<>(); + + for (BookmarkItem item : math.recipeResults) { + if (math.outputRecipes.containsKey(item.recipeId)) { + long amount = item.factor * math.outputRecipes.get(item.recipeId); + rootIngredients.add( + BookmarkItem.of( + -1, + item.getItemStack(amount), + item.getStackSize(amount), + ROOT_RECIPE_ID, + true, + BookmarkItem.generatePermutations(item.itemStack, null))); + } + } + + math.outputRecipes.clear(); + math.outputRecipes.put(ROOT_RECIPE_ID, 1L); + math.recipeResults.add(BookmarkItem.of(-1, ROOT_ITEM, 1, ROOT_RECIPE_ID, false)); + math.recipeIngredients.addAll(rootIngredients); + + return math; + } + + private List prepareInitialItems(RecipeChainMath math, ItemStackAmount inventory) { + final List initialItems = new ArrayList<>(); + + for (BookmarkItem item : math.initialItems) { + final long invStackSize = inventory.getOrDefault(item.itemStack, 0L); + final long amount = Math.max(0, item.amount - invStackSize * item.fluidCellAmount); + if (amount > 0) { + initialItems.add(item.copyWithAmount(amount)); + } + } + + return initialItems; + } + + }; + + private AutoCraftingManager() {} + + public static void runProcessing(RecipeChainMath math) { + task.stop(); + AutoCraftingManager.math = math; + + if (AutoCraftingManager.math != null) { + task.restart(); + } + } + + public static boolean processing() { + return AutoCraftingManager.math != null && !task.interrupted(); + } + +} diff --git a/src/main/java/codechicken/nei/recipe/BookmarkRecipeId.java b/src/main/java/codechicken/nei/recipe/BookmarkRecipeId.java index bcba5c78..88c8eb6e 100644 --- a/src/main/java/codechicken/nei/recipe/BookmarkRecipeId.java +++ b/src/main/java/codechicken/nei/recipe/BookmarkRecipeId.java @@ -14,6 +14,7 @@ import com.google.gson.JsonPrimitive; import codechicken.nei.PositionedStack; import codechicken.nei.util.NBTJson; +@Deprecated public class BookmarkRecipeId { public String handlerName = null; diff --git a/src/main/java/codechicken/nei/recipe/DefaultOverlayHandler.java b/src/main/java/codechicken/nei/recipe/DefaultOverlayHandler.java index fc9c2d51..d30e39a1 100644 --- a/src/main/java/codechicken/nei/recipe/DefaultOverlayHandler.java +++ b/src/main/java/codechicken/nei/recipe/DefaultOverlayHandler.java @@ -7,15 +7,20 @@ import java.util.Iterator; import java.util.LinkedList; import java.util.List; +import net.minecraft.client.entity.EntityClientPlayerMP; import net.minecraft.client.gui.inventory.GuiContainer; import net.minecraft.entity.player.InventoryPlayer; import net.minecraft.inventory.Slot; +import net.minecraft.inventory.SlotCrafting; import net.minecraft.item.ItemStack; import codechicken.lib.inventory.InventoryUtils; import codechicken.nei.FastTransferManager; +import codechicken.nei.NEIClientUtils; +import codechicken.nei.NEIServerUtils; import codechicken.nei.PositionedStack; import codechicken.nei.api.IOverlayHandler; +import codechicken.nei.util.NBTHelper; import cpw.mods.fml.relauncher.ReflectionHelper; public class DefaultOverlayHandler implements IOverlayHandler { @@ -70,44 +75,90 @@ public class DefaultOverlayHandler implements IOverlayHandler { public int offsety; @Override - public void overlayRecipe(GuiContainer gui, IRecipeHandler recipe, int recipeIndex, boolean shift) { - List ingredients = recipe.getIngredientStacks(recipeIndex); - List ingredStacks = getPermutationIngredients(ingredients); + public void overlayRecipe(GuiContainer gui, IRecipeHandler handler, int recipeIndex, boolean maxTransfer) { + transferRecipe(gui, handler, recipeIndex, maxTransfer ? Integer.MAX_VALUE : 1); + } - if (!clearIngredients(gui, ingredients)) return; + @Override + public int transferRecipe(GuiContainer gui, IRecipeHandler handler, int recipeIndex, int multiplier) { + final List ingredients = handler.getIngredientStacks(recipeIndex); + final List ingredStacks = getPermutationIngredients(ingredients); + + if (!clearIngredients(gui)) return 0; findInventoryQuantities(gui, ingredStacks); - List assignedIngredients = assignIngredients(ingredients, ingredStacks); - if (assignedIngredients == null) return; + final List assignedIngredients = assignIngredients(ingredients, ingredStacks); + if (assignedIngredients == null) return 0; assignIngredSlots(gui, ingredients, assignedIngredients); - int quantity = calculateRecipeQuantity(assignedIngredients); + multiplier = Math.min(multiplier == 0 ? 64 : multiplier, calculateRecipeQuantity(assignedIngredients)); - if (quantity != 0) moveIngredients(gui, assignedIngredients, quantity); + if (multiplier != 0) { + moveIngredients(gui, assignedIngredients, (int) multiplier); + } + + return multiplier; } - private boolean clearIngredients(GuiContainer gui, List ingreds) { - for (PositionedStack pstack : ingreds) { - for (Slot slot : gui.inventorySlots.inventorySlots) { - if (slot.xDisplayPosition == pstack.relx + offsetx && slot.yDisplayPosition == pstack.rely + offsety) { - if (!slot.getHasStack()) continue; + @Override + public boolean canFillCraftingGrid(GuiContainer firstGui, IRecipeHandler handler, int recipeIndex) { + return presenceOverlay(firstGui, handler, recipeIndex).stream().allMatch(state -> state.isPresent()); + } - FastTransferManager.clickSlot(gui, slot.slotNumber, 0, 1); - if (slot.getHasStack()) return false; + @Override + public boolean canCraft(GuiContainer firstGui, IRecipeHandler handler, int recipeIndex) { + return canFillCraftingGrid(firstGui, handler, recipeIndex); + } + + @Override + public boolean craft(GuiContainer firstGui, IRecipeHandler recipe, int recipeIndex, int multiplier) { + final EntityClientPlayerMP thePlayer = NEIClientUtils.mc().thePlayer; + boolean craft = false; + + while (multiplier > 0) { + final int transfer = transferRecipe(firstGui, recipe, recipeIndex, multiplier); + + if (transfer <= 0) { + break; + } + + multiplier -= transfer; + + for (Slot slot : firstGui.inventorySlots.inventorySlots) { + if (slot.getHasStack() && slot instanceof SlotCrafting && slot.canTakeStack(thePlayer)) { + FastTransferManager.clickSlot(firstGui, slot.slotNumber, 0, 1); + craft = true; + break; } } + + } + + return craft; + } + + private boolean clearIngredients(GuiContainer gui) { + final EntityClientPlayerMP thePlayer = NEIClientUtils.mc().thePlayer; + + for (Slot slot : gui.inventorySlots.inventorySlots) { + if (slot.getHasStack() && !canMoveFrom(slot, gui) + && !(slot instanceof SlotCrafting) + && slot.canTakeStack(thePlayer)) { + FastTransferManager.clickSlot(gui, slot.slotNumber, 0, 1); + if (slot.getHasStack()) return false; + } } return true; } - private void moveIngredients(GuiContainer gui, List assignedIngredients, int quantity) { + private void moveIngredients(GuiContainer gui, List assignedIngredients, int multiplier) { for (IngredientDistribution distrib : assignedIngredients) { if (distrib.slots.length == 0) continue; ItemStack pstack = distrib.permutation; - int transferCap = quantity * pstack.stackSize; + int transferCap = multiplier * pstack.stackSize; int transferred = 0; int destSlotIndex = 0; @@ -119,7 +170,7 @@ public class DefaultOverlayHandler implements IOverlayHandler { if (!slot.getHasStack() || !canMoveFrom(slot, gui)) continue; ItemStack stack = slot.getStack(); - if (!canStack(stack, pstack)) continue; + if (!canStack(pstack, stack)) continue; int amount = Math.min(transferCap - transferred, stack.stackSize); FastTransferManager.clickSlot(gui, slot.slotNumber); @@ -145,6 +196,7 @@ public class DefaultOverlayHandler implements IOverlayHandler { private int calculateRecipeQuantity(List assignedIngredients) { int quantity = Integer.MAX_VALUE; + for (IngredientDistribution distrib : assignedIngredients) { DistributedIngred istack = distrib.distrib; if (istack.numSlots == 0) return 0; @@ -160,6 +212,7 @@ public class DefaultOverlayHandler implements IOverlayHandler { } quantity = Math.min(quantity, newQuantity); } + if (quantity == Integer.MAX_VALUE) { // Only possible if all ingredients were non-stackable quantity = 1; @@ -292,14 +345,14 @@ public class DefaultOverlayHandler implements IOverlayHandler { } public DistributedIngred findIngred(List ingredStacks, ItemStack pstack) { - for (DistributedIngred istack : ingredStacks) if (canStack(pstack, istack.stack)) return istack; + for (DistributedIngred istack : ingredStacks) if (canStack(istack.stack, pstack)) return istack; return null; } protected boolean canStack(ItemStack stack1, ItemStack stack2) { if (stack1 == null || stack2 == null) return true; - if (stack1.getItem() == stack2.getItem() && stack1.getItemDamage() == stack2.getItemDamage()) { - if (ItemStack.areItemStackTagsEqual(stack2, stack1)) return true; + if (NEIServerUtils.areStacksSameTypeCrafting(stack2, stack1)) { + if (NBTHelper.matchTag(stack1.getTagCompound(), stack2.getTagCompound())) return true; // GT Items don't have any NBT set for the recipe, so if either of the stacks has a NULL nbt, and the other // doesn't, pretend they stack diff --git a/src/main/java/codechicken/nei/recipe/FillFluidContainerHandler.java b/src/main/java/codechicken/nei/recipe/FillFluidContainerHandler.java index bc30a432..c2982ed1 100644 --- a/src/main/java/codechicken/nei/recipe/FillFluidContainerHandler.java +++ b/src/main/java/codechicken/nei/recipe/FillFluidContainerHandler.java @@ -6,7 +6,6 @@ import net.minecraftforge.fluids.FluidContainerRegistry; import net.minecraftforge.fluids.FluidStack; import net.minecraftforge.fluids.IFluidContainerItem; -import codechicken.nei.ItemPanel.ItemPanelSlot; import codechicken.nei.ItemPanels; import codechicken.nei.api.INEIGuiAdapter; @@ -19,18 +18,18 @@ public class FillFluidContainerHandler extends INEIGuiAdapter { return false; } - if (draggedStack.getItem() instanceof IFluidContainerItem || FluidContainerRegistry.isContainer(draggedStack)) { - ItemPanelSlot mouseOverSlot = ItemPanels.itemPanel.getSlotMouseOver(mouseX, mouseY); + if (StackInfo.isFluidContainer(draggedStack)) { + ItemStack overItemStack = ItemPanels.itemPanel.getStackMouseOver(mouseX, mouseY); - if (mouseOverSlot == null) { - mouseOverSlot = ItemPanels.bookmarkPanel.getSlotMouseOver(mouseX, mouseY); + if (overItemStack == null) { + overItemStack = ItemPanels.bookmarkPanel.getStackMouseOver(mouseX, mouseY); } - if (mouseOverSlot != null) { - FluidStack fluidStack = StackInfo.getFluid(mouseOverSlot.item); + if (overItemStack != null) { + final FluidStack fluidStack = StackInfo.getFluid(overItemStack); if (fluidStack != null) { - ItemStack container = fillContainer(draggedStack, fluidStack); + final ItemStack container = fillContainer(draggedStack, fluidStack); if (container != null) { container.stackSize = draggedStack.stackSize; @@ -43,7 +42,7 @@ public class FillFluidContainerHandler extends INEIGuiAdapter { } if (button == 1 && StackInfo.getFluid(container != null ? container : draggedStack) != null) { - ItemPanels.bookmarkPanel.addOrRemoveItem((container != null ? container : draggedStack).copy()); + ItemPanels.bookmarkPanel.addItem(container != null ? container : draggedStack); } } diff --git a/src/main/java/codechicken/nei/recipe/GuiCraftingRecipe.java b/src/main/java/codechicken/nei/recipe/GuiCraftingRecipe.java index 09ef8702..62d5e62a 100644 --- a/src/main/java/codechicken/nei/recipe/GuiCraftingRecipe.java +++ b/src/main/java/codechicken/nei/recipe/GuiCraftingRecipe.java @@ -4,7 +4,6 @@ import java.awt.Point; import java.util.ArrayList; import java.util.Arrays; import java.util.HashSet; -import java.util.List; import java.util.Set; import java.util.function.UnaryOperator; import java.util.stream.Collectors; @@ -14,11 +13,12 @@ import net.minecraft.client.gui.GuiScreen; import net.minecraft.item.ItemStack; import codechicken.lib.gui.GuiDraw; -import codechicken.nei.ItemPanel.ItemPanelSlot; +import codechicken.nei.FavoriteRecipes; import codechicken.nei.ItemPanels; import codechicken.nei.NEIClientConfig; import codechicken.nei.NEIClientUtils; -import codechicken.nei.PositionedStack; +import codechicken.nei.bookmark.BookmarksGridSlot; +import codechicken.nei.recipe.Recipe.RecipeId; import codechicken.nei.recipe.stackinfo.GTFluidStackStringifyHandler; public class GuiCraftingRecipe extends GuiRecipe { @@ -33,7 +33,7 @@ public class GuiCraftingRecipe extends GuiRecipe { public static GuiRecipe createRecipeGui(String outputId, boolean open, Object... results) { final Minecraft mc = NEIClientUtils.mc(); - final BookmarkRecipeId recipeId; + final RecipeId recipeId; if ("item".equals(outputId)) { for (int i = 0; i < results.length; i++) { @@ -41,7 +41,7 @@ public class GuiCraftingRecipe extends GuiRecipe { } recipeId = getRecipeId(mc.currentScreen, (ItemStack) results[0]); } else if ("recipeId".equals(outputId)) { - recipeId = (BookmarkRecipeId) results[1]; + recipeId = (RecipeId) results[1]; } else { recipeId = getCurrentRecipeId(mc.currentScreen); } @@ -62,28 +62,6 @@ public class GuiCraftingRecipe extends GuiRecipe { return null; } - public static boolean overlayRecipe(ItemStack stack, BookmarkRecipeId recipeId, final Boolean shift) { - - if (stack == null || recipeId == null) { - return false; - } - - final ArrayList handlers = getCraftingHandlers("recipeId", stack, recipeId); - - if (!handlers.isEmpty()) { - final GuiCraftingRecipe gui = new GuiCraftingRecipe(handlers, true); - final int recipe = gui.openTargetRecipe(recipeId); - - if (recipe != -1) { - gui.overlayRecipe(recipe, shift); - return true; - } - - } - - return false; - } - public static ArrayList getCraftingHandlers(String outputId, Object... results) { ArrayList craftinghandlers = GuiCraftingRecipe.craftinghandlers; ArrayList serialCraftingHandlers = GuiCraftingRecipe.serialCraftingHandlers; @@ -91,9 +69,9 @@ public class GuiCraftingRecipe extends GuiRecipe { if ("recipeId".equals(outputId)) { ItemStack stack = (ItemStack) results[0]; - BookmarkRecipeId recipeId = (BookmarkRecipeId) results[1]; - craftinghandlers = filterByHandlerName(craftinghandlers, recipeId.handlerName); - serialCraftingHandlers = filterByHandlerName(serialCraftingHandlers, recipeId.handlerName); + RecipeId recipeId = (RecipeId) results[1]; + craftinghandlers = filterByHandlerName(craftinghandlers, recipeId.getHandleName()); + serialCraftingHandlers = filterByHandlerName(serialCraftingHandlers, recipeId.getHandleName()); recipeHandlerFunction = h -> h.getRecipeHandler("item", stack); } else { recipeHandlerFunction = h -> h.getRecipeHandler(outputId, results); @@ -127,25 +105,29 @@ public class GuiCraftingRecipe extends GuiRecipe { return stack; } - protected static BookmarkRecipeId getRecipeId(GuiScreen gui, ItemStack stackover) { + public static RecipeId getRecipeId(GuiScreen gui, ItemStack stackover) { - if (gui instanceof GuiRecipe) { - final List ingredients = ((GuiRecipe) gui).getFocusedRecipeIngredients(); - final String handlerName = ((GuiRecipe) gui).getHandlerName(); + if (gui instanceof GuiRecipe guiRecipe) { + final Recipe focusedRecipe = guiRecipe.getFocusedRecipe(); - if (ingredients != null && !ingredients.isEmpty()) { - return new BookmarkRecipeId(handlerName, ingredients); + if (focusedRecipe != null) { + return focusedRecipe.getRecipeId(); } } final Point mouseover = GuiDraw.getMousePosition(); - ItemPanelSlot panelSlot = ItemPanels.bookmarkPanel.getSlotMouseOver(mouseover.x, mouseover.y); + final BookmarksGridSlot panelSlot = ItemPanels.bookmarkPanel.getSlotMouseOver(mouseover.x, mouseover.y); + RecipeId recipeId = null; - if (panelSlot != null) { - return ItemPanels.bookmarkPanel.getBookmarkRecipeId(panelSlot.slotIndex); + if (panelSlot != null && !panelSlot.isIngredient()) { + recipeId = panelSlot.getRecipeId(); } - return ItemPanels.bookmarkPanel.getBookmarkRecipeId(stackover); + if (recipeId == null) { + recipeId = FavoriteRecipes.getFavorite(stackover); + } + + return recipeId; } private GuiCraftingRecipe(ArrayList handlers, boolean limitToOneRecipe) { diff --git a/src/main/java/codechicken/nei/recipe/GuiFavoriteButton.java b/src/main/java/codechicken/nei/recipe/GuiFavoriteButton.java new file mode 100644 index 00000000..868be0d9 --- /dev/null +++ b/src/main/java/codechicken/nei/recipe/GuiFavoriteButton.java @@ -0,0 +1,249 @@ +package codechicken.nei.recipe; + +import static codechicken.nei.NEIClientUtils.translate; + +import java.util.ArrayList; +import java.util.HashSet; +import java.util.List; +import java.util.Map; +import java.util.Set; + +import net.minecraft.client.Minecraft; +import net.minecraft.client.gui.inventory.GuiContainer; +import net.minecraft.item.ItemStack; + +import org.lwjgl.opengl.GL11; + +import codechicken.lib.gui.GuiDraw; +import codechicken.nei.BookmarkPanel.BookmarkViewMode; +import codechicken.nei.FavoriteRecipes; +import codechicken.nei.Image; +import codechicken.nei.ItemPanels; +import codechicken.nei.ItemStackSet; +import codechicken.nei.LayoutManager; +import codechicken.nei.NEIClientConfig; +import codechicken.nei.NEIClientUtils; +import codechicken.nei.drawable.DrawableBuilder; +import codechicken.nei.drawable.DrawableResource; +import codechicken.nei.recipe.Recipe.RecipeId; +import codechicken.nei.recipe.Recipe.RecipeIngredient; +import codechicken.nei.util.NEIMouseUtils; + +public class GuiFavoriteButton extends GuiRecipeButton { + + private static final int BUTTON_ID_START = 14; + + protected static final DrawableResource ICON_STATE_OFF = new DrawableBuilder( + "nei:textures/nei_sprites.png", + 10, + 76, + 9, + 10).build(); + protected static final DrawableResource ICON_STATE_ON = new DrawableBuilder( + "nei:textures/nei_sprites.png", + 19, + 76, + 9, + 10).build(); + + protected final Recipe recipe; + protected boolean favorite = false; + protected RecipeIngredient favoriteResult = null; + protected RecipeIngredient selectedResult = null; + + public GuiFavoriteButton(RecipeHandlerRef handlerRef, int x, int y) { + super(handlerRef, x, y, BUTTON_ID_START + handlerRef.recipeIndex, "❤"); + this.recipe = Recipe.of(this.handlerRef); + + ItemStack stack = FavoriteRecipes.getFavorite(this.recipe.getRecipeId()); + this.favorite = stack != null; + + if (stack == null) { + stack = this.recipe.getResult(); + } + + for (RecipeIngredient result : this.recipe.getResults()) { + if (StackInfo.equalItemAndNBT(result.getItemStack(), stack, true)) { + this.favoriteResult = result; + break; + } + } + + this.visible = this.favoriteResult != null; + } + + public boolean isFavorite() { + return favorite; + } + + public void toggleFavorite() { + this.favorite = !this.favorite; + + if (this.selectedResult != null) { + this.favoriteResult = this.selectedResult; + } + + FavoriteRecipes + .setFavorite(this.favoriteResult.getItemStack(), this.favorite ? this.recipe.getRecipeId() : null); + } + + @Override + protected void drawContent(Minecraft minecraft, int y, int x, boolean mouseOver) { + final DrawableResource icon = isFavorite() ? ICON_STATE_ON : ICON_STATE_OFF; + final int iconX = this.xPosition + (this.width - icon.width - 1) / 2; + final int iconY = this.yPosition + (this.height - icon.height) / 2; + + GL11.glColor4f(1, 1, 1, this.enabled ? 1 : 0.5f); + GL11.glEnable(GL11.GL_BLEND); + GL11.glBlendFunc(GL11.GL_SRC_ALPHA, GL11.GL_ONE_MINUS_SRC_ALPHA); + icon.draw(iconX, iconY); + GL11.glDisable(GL11.GL_BLEND); + GL11.glColor4f(1, 1, 1, 1); + + if (!mouseOver) { + this.selectedResult = null; + } else if (this.selectedResult == null) { + this.selectedResult = this.favoriteResult; + } + } + + @Override + public void mouseReleased(int mouseX, int mouseY) { + toggleFavorite(); + } + + public Recipe getRecipe() { + return Recipe.of(this.handlerRef); + } + + public void lastKeyTyped(GuiRecipe gui, char keyChar, int keyID) { + + if (NEIClientConfig.isKeyHashDown("gui.bookmark") && NEIClientUtils.shiftKey()) { + ItemPanels.bookmarkPanel.addGroup(getRecipesTree(getRecipe()), BookmarkViewMode.TODO_LIST, true); + } + + } + + @Override + public Map handleHotkeys(GuiContainer gui, int mousex, int mousey, Map hotkeys) { + hotkeys.put( + NEIClientConfig.getKeyName("gui.bookmark", NEIClientUtils.SHIFT_HASH), + translate("recipe.favorite.bookmark_recipe")); + + if (this.recipe.getResults().size() > 1) { + hotkeys.put( + NEIMouseUtils.getKeyName(NEIMouseUtils.MOUSE_BTN_NONE + NEIMouseUtils.MOUSE_SCROLL), + translate("recipe.favorite.change_result_index")); + } + + return hotkeys; + } + + @Override + public List handleTooltip(GuiRecipe gui, List currenttip) { + currenttip.add(translate("recipe.favorite")); + + return currenttip; + } + + @Override + public void drawItemOverlay() { + if (this.selectedResult == null) return; + + NEIClientUtils.gl2DRenderContext( + () -> GuiDraw.drawRect(this.selectedResult.relx, this.selectedResult.rely, 16, 16, 0x66333333)); + + final Image icon = isFavorite() ? ICON_STATE_ON : ICON_STATE_OFF; + final int iconX = this.selectedResult.relx + (16 - icon.width) / 2; + final int iconY = this.selectedResult.rely + (18 - icon.height) / 2; + + LayoutManager.drawIcon(iconX, iconY, icon); + } + + @Override + public boolean mouseScrolled(GuiRecipe gui, int scroll) { + if (this.selectedResult == null) return true; + + final List results = new ArrayList<>(this.recipe.getResults()); + if (results.size() <= 1) return true; + + final Set uniqueResults = new HashSet<>(); + results.removeIf(ingr -> !uniqueResults.add(StackInfo.getItemStackGUID(ingr.getItemStack()))); + final int nextIndex = (results.size() - scroll + results.indexOf(this.selectedResult)) % results.size(); + + this.selectedResult = results.get(nextIndex); + this.favorite = StackInfo.equalItemAndNBT( + results.get(nextIndex).getItemStack(), + FavoriteRecipes.getFavorite(this.recipe.getRecipeId()), + true); + + return true; + } + + private List getRecipesTree(Recipe mainRecipe) { + int depth = NEIClientConfig.getIntSetting("inventory.favorites.depth"); + final ItemStackSet items = new ItemStackSet(); + final List result = new ArrayList<>(); + List loop = new ArrayList<>(); + loop.add(mainRecipe); + result.add(mainRecipe); + + while (!loop.isEmpty() && depth-- >= 0) { + List localLoop = new ArrayList<>(); + + for (Recipe localRecipe : loop) { + for (RecipeIngredient ingr : localRecipe.getIngredients()) { + List permutations = ingr.getPermutations(); + ItemStack stack = permutations.stream().filter(items::contains).findFirst().orElse(null); + + if (stack == null) { + stack = ingr.getItemStack(); + RecipeId recipeId = FavoriteRecipes.getFavorite(stack); + + if (recipeId == null) { + for (ItemStack permStack : permutations) { + if ((recipeId = FavoriteRecipes.getFavorite(permStack)) != null) { + stack = permStack; + break; + } + } + } + + Recipe recipe = Recipe.of(recipeId); + + if (recipe == null) { + stack = null; + } else { + items.add(stack); + + if (depth >= 0) { + result.add(recipe); + localLoop.add(recipe); + } + } + } + + if (stack != null) { + ingr.setActiveIndex(getIngrActiveIndex(permutations, stack)); + } + } + } + + loop = localLoop; + } + + return result; + } + + private int getIngrActiveIndex(List permutations, ItemStack stack) { + + for (int i = 0; i < permutations.size(); i++) { + if (StackInfo.equalItemAndNBT(stack, permutations.get(i), true)) { + return i; + } + } + + return 0; + } + +} diff --git a/src/main/java/codechicken/nei/recipe/GuiOverlayButton.java b/src/main/java/codechicken/nei/recipe/GuiOverlayButton.java index d50e0566..96797041 100644 --- a/src/main/java/codechicken/nei/recipe/GuiOverlayButton.java +++ b/src/main/java/codechicken/nei/recipe/GuiOverlayButton.java @@ -1,66 +1,31 @@ package codechicken.nei.recipe; -import static codechicken.nei.NEIClientUtils.translate; - import java.util.ArrayList; import java.util.List; import java.util.Map; import java.util.Optional; import java.util.stream.Collectors; +import net.minecraft.client.Minecraft; import net.minecraft.client.gui.inventory.GuiContainer; import net.minecraft.item.ItemStack; import net.minecraft.util.EnumChatFormatting; -import net.minecraftforge.client.event.GuiScreenEvent; + +import org.lwjgl.opengl.GL11; import codechicken.lib.gui.GuiDraw; -import codechicken.lib.vec.Rectangle4i; -import codechicken.nei.BookmarkPanel.BookmarkRecipe; -import codechicken.nei.GuiNEIButton; import codechicken.nei.ItemPanels; import codechicken.nei.ItemsTooltipLineHandler; import codechicken.nei.LayoutManager; import codechicken.nei.NEIClientConfig; import codechicken.nei.NEIClientUtils; import codechicken.nei.PositionedStack; -import codechicken.nei.api.IOverlayHandler; -import cpw.mods.fml.common.eventhandler.Cancelable; +import codechicken.nei.bookmark.BookmarkGrid; +import codechicken.nei.drawable.DrawableBuilder; +import codechicken.nei.drawable.DrawableResource; +import codechicken.nei.util.NEIMouseUtils; -public class GuiOverlayButton extends GuiNEIButton { - - public static class UpdateOverlayButtonsEvent extends GuiScreenEvent { - - public List buttonList; - - public UpdateOverlayButtonsEvent(GuiRecipe gui, List buttonList) { - super(gui); - this.buttonList = new ArrayList<>(buttonList); - } - - @Cancelable - public static class Pre extends UpdateOverlayButtonsEvent { - - public int xOffset; - public int yOffset; - public int height; - public HandlerInfo handlerInfo; - - public Pre(GuiRecipe gui, int xOffset, int yOffset, int height, HandlerInfo handlerInfo) { - super(gui, new ArrayList<>()); - this.xOffset = xOffset; - this.yOffset = yOffset; - this.height = height; - this.handlerInfo = handlerInfo; - } - } - - public static class Post extends UpdateOverlayButtonsEvent { - - public Post(GuiRecipe gui, List buttonList) { - super(gui, buttonList); - } - } - } +public class GuiOverlayButton extends GuiRecipeButton { public enum ItemOverlayFormat { @@ -99,44 +64,59 @@ public class GuiOverlayButton extends GuiNEIButton { } } - private static final int OVERLAY_BUTTON_ID_START = 4; + protected static final DrawableResource ICON_FILL = new DrawableBuilder( + "nei:textures/nei_sprites.png", + 28, + 76, + 9, + 10).build(); + protected static final DrawableResource ICON_FILL_ERROR = new DrawableBuilder( + "nei:textures/nei_sprites.png", + 37, + 76, + 9, + 10).build(); + protected static final DrawableResource ICON_OVERLAY = new DrawableBuilder( + "nei:textures/nei_sprites.png", + 46, + 76, + 9, + 10).build(); + protected static final int BUTTON_ID_SHIFT = 4; public final GuiContainer firstGui; - public final IRecipeHandler handler; - public final int recipeIndex; - protected ItemsTooltipLineHandler missedMaterialsTooltipLineHandler; - - protected final IOverlayHandler overlayHandler; protected final List itemPresenceCache = new ArrayList<>(); + protected ItemsTooltipLineHandler missedMaterialsTooltipLineHandler; protected boolean requireShiftForOverlayRecipe = true; - protected boolean useOverlayRenderer = false; + protected boolean canUseOverlayRenderer = false; + protected boolean canFillCraftingGrid = false; protected boolean hasOverlay = false; - public GuiOverlayButton(GuiContainer firstGui, IRecipeHandler handler, int recipeIndex, int x, int y, int width, - int height) { - super(OVERLAY_BUTTON_ID_START + recipeIndex, x, y, width, height, "+"); + public GuiOverlayButton(GuiContainer firstGui, RecipeHandlerRef handlerRef, int x, int y) { + super(handlerRef, x, y, handlerRef.recipeIndex + BUTTON_ID_SHIFT, "+"); this.firstGui = firstGui != null && firstGui.inventorySlots != null ? firstGui : null; - this.handler = handler; - this.recipeIndex = recipeIndex; - this.hasOverlay = this.firstGui != null - && handler.hasOverlay(this.firstGui, this.firstGui.inventorySlots, recipeIndex); - this.useOverlayRenderer = this.hasOverlay && handler.getOverlayRenderer(firstGui, recipeIndex) != null; - this.overlayHandler = this.hasOverlay ? handler.getOverlayHandler(firstGui, recipeIndex) : null; + + this.canUseOverlayRenderer = this.firstGui != null && handlerRef.getRecipeOverlayRenderer(firstGui) != null; + this.canFillCraftingGrid = this.firstGui != null && handlerRef.getOverlayHandler(firstGui) != null; + this.hasOverlay = this.canUseOverlayRenderer || this.canFillCraftingGrid; setRequireShiftForOverlayRecipe(NEIClientConfig.requireShiftForOverlayRecipe()); ingredientsOverlay(); } + @Override public List handleTooltip(GuiRecipe gui, List currenttip) { - currenttip.add(translate("recipe.overlay")); + currenttip.add(NEIClientUtils.translate("recipe.overlay")); - if (!this.enabled && this.requireShiftForOverlayRecipe && this.overlayHandler == null) { + if (!this.enabled && this.requireShiftForOverlayRecipe && this.canFillCraftingGrid) { currenttip.set(currenttip.size() - 1, currenttip.get(currenttip.size() - 1) + GuiDraw.TOOLTIP_LINESPACE); - currenttip.add(EnumChatFormatting.RED + translate("recipe.overlay.mismatch") + EnumChatFormatting.RESET); + currenttip.add( + EnumChatFormatting.RED + NEIClientUtils.translate("recipe.overlay.mismatch") + + EnumChatFormatting.RESET); } else if (this.missedMaterialsTooltipLineHandler != null) { - if ((!this.requireShiftForOverlayRecipe || NEIClientUtils.shiftKey()) && this.overlayHandler != null) { + if ((!this.requireShiftForOverlayRecipe || NEIClientUtils.shiftKey()) && this.canFillCraftingGrid) { this.missedMaterialsTooltipLineHandler.setLabelColor(EnumChatFormatting.RED); } else { this.missedMaterialsTooltipLineHandler.setLabelColor(EnumChatFormatting.GRAY); @@ -148,31 +128,32 @@ public class GuiOverlayButton extends GuiNEIButton { return currenttip; } + @Override public Map handleHotkeys(GuiContainer gui, int mousex, int mousey, Map hotkeys) { - if (this.requireShiftForOverlayRecipe && this.useOverlayRenderer) { - hotkeys.put(translate("recipe.overlay.fillCraftingGrid.key"), translate("recipe.overlay.fillCraftingGrid")); + if (requireShiftForOverlayRecipe()) { + + if (useOverlayRenderer()) { + hotkeys.put( + NEIMouseUtils.getKeyName(NEIMouseUtils.MOUSE_BTN_LMB), + NEIClientUtils.translate("recipe.overlay.overlayRecipe")); + } + + if (canFillCraftingGrid()) { + hotkeys.put( + NEIClientUtils.getKeyName(NEIClientUtils.SHIFT_HASH, NEIMouseUtils.MOUSE_BTN_LMB), + NEIClientUtils.translate("recipe.overlay.fillCraftingGrid")); + } + + } else if (canFillCraftingGrid()) { + hotkeys.put( + NEIMouseUtils.getKeyName(NEIMouseUtils.MOUSE_BTN_LMB), + NEIClientUtils.translate("recipe.overlay.fillCraftingGrid")); } - String keyName = NEIClientConfig.getKeyName("gui.bookmark"); - if (keyName != null) { - hotkeys.put(keyName, translate("recipe.overlay.bookmark")); - } - - keyName = NEIClientConfig.getKeyName("gui.bookmark_recipe"); - if (keyName != null) { - hotkeys.put(keyName, translate("recipe.overlay.bookmarkRecipe")); - } - - keyName = NEIClientConfig.getKeyName("gui.bookmark_count"); - if (keyName != null) { - hotkeys.put(keyName, translate("recipe.overlay.bookmarkAmount")); - } - - keyName = NEIClientConfig.getKeyName("gui.bookmark_recipe_count"); - if (keyName != null) { - hotkeys.put(keyName, translate("recipe.overlay.bookmarkRecipeAndAmount")); - } + hotkeys.put( + NEIClientConfig.getKeyName("gui.bookmark", NEIClientUtils.SHIFT_HASH), + NEIClientUtils.translate("recipe.overlay.bookmarkRecipe")); return hotkeys; } @@ -185,37 +166,59 @@ public class GuiOverlayButton extends GuiNEIButton { } } - public void drawItemPresenceOverlay() { - final int presenceOverlay = NEIClientConfig.itemPresenceOverlay(); - final boolean highlightPresentItem = NEIClientConfig.isSlotHighlightPresent(); - final ItemOverlayFormat format = ItemOverlayFormat.from(presenceOverlay); + @Override + protected void drawContent(Minecraft minecraft, int y, int x, boolean mouseOver) { + DrawableResource icon = ICON_OVERLAY; - for (ItemOverlayState overlay : ingredientsOverlay()) { - if (highlightPresentItem || !overlay.isPresent()) { - overlay.draw(format); + if (this.hasOverlay && (!this.requireShiftForOverlayRecipe || NEIClientUtils.shiftKey())) { + icon = canFillCraftingGrid() ? ICON_FILL : ICON_FILL_ERROR; + } + + final int iconX = this.xPosition + (this.width - icon.width - 1) / 2; + final int iconY = this.yPosition + (this.height - icon.height) / 2; + + GL11.glColor4f(1, 1, 1, this.enabled ? 1 : 0.5f); + GL11.glEnable(GL11.GL_BLEND); + GL11.glBlendFunc(GL11.GL_SRC_ALPHA, GL11.GL_ONE_MINUS_SRC_ALPHA); + icon.draw(iconX, iconY); + GL11.glDisable(GL11.GL_BLEND); + GL11.glColor4f(1, 1, 1, 1); + } + + @Override + public void drawItemOverlay() { + final int presenceOverlay = NEIClientConfig.itemPresenceOverlay(); + + if (presenceOverlay > 0) { + final boolean highlightPresentItem = NEIClientConfig.isSlotHighlightPresent(); + final ItemOverlayFormat format = ItemOverlayFormat.from(presenceOverlay); + + for (ItemOverlayState overlay : ingredientsOverlay()) { + if (highlightPresentItem || !overlay.isPresent()) { + overlay.draw(format); + } } } } protected List ingredientsOverlay() { - List ingredients = this.handler.getIngredientStacks(recipeIndex); + List ingredients = this.handlerRef.handler.getIngredientStacks(this.handlerRef.recipeIndex); if (this.itemPresenceCache.size() != ingredients.size()) { this.itemPresenceCache.clear(); - if (this.overlayHandler != null) { - this.itemPresenceCache - .addAll(this.overlayHandler.presenceOverlay(this.firstGui, this.handler, this.recipeIndex)); + if (this.canFillCraftingGrid) { + this.itemPresenceCache.addAll(this.handlerRef.getPresenceOverlay(this.firstGui)); } else if (this.firstGui != null) { this.itemPresenceCache.addAll(presenceOverlay(ingredients)); } List items = this.itemPresenceCache.stream().filter(state -> !state.isPresent()) - .map(state -> state.getSlot().item).collect(Collectors.toCollection(ArrayList::new)); + .map(state -> state.getSlot().item).collect(Collectors.toList()); if (!items.isEmpty()) { this.missedMaterialsTooltipLineHandler = new ItemsTooltipLineHandler( - translate("recipe.overlay.missing"), + NEIClientUtils.translate("recipe.overlay.missing"), items, true, Integer.MAX_VALUE); @@ -229,25 +232,33 @@ public class GuiOverlayButton extends GuiNEIButton { public void setRequireShiftForOverlayRecipe(boolean require) { this.requireShiftForOverlayRecipe = require; - this.enabled = this.hasOverlay && (this.requireShiftForOverlayRecipe || this.overlayHandler != null); + this.enabled = this.hasOverlay && (this.requireShiftForOverlayRecipe || this.canFillCraftingGrid); } - public void setUseOverlayRenderer(boolean use) { - this.useOverlayRenderer = use; + public void setCanUseOverlayRenderer(boolean use) { + this.canUseOverlayRenderer = use; } public boolean useOverlayRenderer() { - return this.useOverlayRenderer; + return this.canUseOverlayRenderer; + } + + public boolean requireShiftForOverlayRecipe() { + return this.requireShiftForOverlayRecipe; } public boolean canFillCraftingGrid() { - return this.missedMaterialsTooltipLineHandler == null; + return this.missedMaterialsTooltipLineHandler == null || this.handlerRef.canFillCraftingGrid(this.firstGui); } public boolean hasOverlay() { return this.hasOverlay; } + public Recipe getRecipe() { + return Recipe.of(this.handlerRef); + } + private List presenceOverlay(List ingredients) { final List itemPresenceSlots = new ArrayList<>(); final List invStacks = this.firstGui.inventorySlots.inventorySlots.stream() @@ -276,82 +287,30 @@ public class GuiOverlayButton extends GuiNEIButton { public void overlayRecipe(boolean shift) { if (!this.hasOverlay) return; - if (!this.useOverlayRenderer || !this.requireShiftForOverlayRecipe || shift) { - if (this.overlayHandler != null) { - this.overlayHandler.overlayRecipe( - this.firstGui, - this.handler, - this.recipeIndex, - !this.requireShiftForOverlayRecipe || shift); + if (!this.requireShiftForOverlayRecipe || shift) { + if (canFillCraftingGrid()) { + this.handlerRef.fillCraftingGrid(this.firstGui, 0); } - } else { - LayoutManager.overlayRenderer = this.handler.getOverlayRenderer(this.firstGui, this.recipeIndex); + } else if (useOverlayRenderer()) { + this.handlerRef.useOverlayRenderer(this.firstGui); } } + @Override public void lastKeyTyped(GuiRecipe gui, char keyChar, int keyID) { - if (NEIClientConfig.isKeyHashDown("gui.bookmark")) { - saveRecipeInBookmark(false, false); - } else if (NEIClientConfig.isKeyHashDown("gui.bookmark_recipe")) { - saveRecipeInBookmark(true, false); - } else if (NEIClientConfig.isKeyHashDown("gui.bookmark_count")) { - saveRecipeInBookmark(false, true); - } else if (NEIClientConfig.isKeyHashDown("gui.bookmark_recipe_count")) { - saveRecipeInBookmark(true, true); - } - - } - - public void saveRecipeInBookmark(boolean saveIngredients, boolean saveStackSize) { - final HandlerInfo handlerInfo = GuiRecipeTab.getHandlerInfo(this.handler); - final List ingredients = this.handler.getIngredientStacks(this.recipeIndex); - final BookmarkRecipeId recipeId = new BookmarkRecipeId(handlerInfo.getHandlerName(), ingredients); - - if (!ItemPanels.bookmarkPanel.removeBookmarkRecipeId(recipeId)) { - BookmarkRecipe recipe = new BookmarkRecipe(); - recipe.handlerName = recipeId.handlerName; - recipe.recipeId = recipeId; - - if (saveIngredients) { - for (PositionedStack stack : ingredients) { - recipe.ingredients.add(stack.item); - } - } - - PositionedStack result = this.handler.getResultStack(this.recipeIndex); - - if (result != null) { - recipe.result.add(result.item); - } else { - for (PositionedStack stack : this.handler.getOtherStacks(this.recipeIndex)) { - recipe.result.add(stack.item); - } - } - - ItemPanels.bookmarkPanel.addRecipe(recipe, saveStackSize); + if (NEIClientConfig.isKeyHashDown("gui.bookmark") && NEIClientUtils.shiftKey()) { + saveRecipeInBookmark(); } } - public int getResultStackSize(ItemStack stackover) { - final List stacks = this.handler.getOtherStacks(this.recipeIndex); - int stackSize = stackover.stackSize; + public void saveRecipeInBookmark() { + final Recipe recipe = getRecipe(); - for (PositionedStack pStack : stacks) { - if (StackInfo.equalItemAndNBT(pStack.item, stackover, true)) { - stackSize += pStack.item.stackSize; - } + if (!ItemPanels.bookmarkPanel.removeRecipe(recipe.getRecipeId(), BookmarkGrid.DEFAULT_GROUP_ID)) { + ItemPanels.bookmarkPanel.addRecipe(recipe, BookmarkGrid.DEFAULT_GROUP_ID); } - - return stackSize; } - public Rectangle4i bounds() { - return new Rectangle4i(this.xPosition, this.yPosition, this.width, this.height); - } - - public boolean contains(int mx, int my) { - return bounds().contains(mx, my); - } } diff --git a/src/main/java/codechicken/nei/recipe/GuiRecipe.java b/src/main/java/codechicken/nei/recipe/GuiRecipe.java index 9894d9df..325a7f66 100644 --- a/src/main/java/codechicken/nei/recipe/GuiRecipe.java +++ b/src/main/java/codechicken/nei/recipe/GuiRecipe.java @@ -19,7 +19,6 @@ import net.minecraft.client.renderer.OpenGlHelper; import net.minecraft.client.renderer.RenderHelper; import net.minecraft.inventory.Slot; import net.minecraft.item.ItemStack; -import net.minecraft.util.EnumChatFormatting; import net.minecraftforge.common.MinecraftForge; import org.lwjgl.input.Keyboard; @@ -28,8 +27,6 @@ import org.lwjgl.opengl.GL11; import codechicken.lib.gui.GuiDraw; import codechicken.nei.Button; import codechicken.nei.GuiNEIButton; -import codechicken.nei.ItemsTooltipLineHandler; -import codechicken.nei.LayoutManager; import codechicken.nei.NEICPH; import codechicken.nei.NEIClientConfig; import codechicken.nei.NEIClientUtils; @@ -40,10 +37,8 @@ import codechicken.nei.SearchField; import codechicken.nei.VisiblityData; import codechicken.nei.api.IGuiContainerOverlay; import codechicken.nei.api.INEIGuiHandler; -import codechicken.nei.api.IOverlayHandler; import codechicken.nei.api.IRecipeFilter; import codechicken.nei.api.IRecipeFilter.IRecipeFilterProvider; -import codechicken.nei.api.IRecipeOverlayRenderer; import codechicken.nei.api.ItemFilter; import codechicken.nei.api.TaggedInventoryArea; import codechicken.nei.drawable.DrawableBuilder; @@ -52,7 +47,9 @@ import codechicken.nei.guihook.GuiContainerManager; import codechicken.nei.guihook.IContainerTooltipHandler; import codechicken.nei.guihook.IGuiClientSide; import codechicken.nei.guihook.IGuiHandleMouseWheel; -import codechicken.nei.recipe.GuiOverlayButton.UpdateOverlayButtonsEvent; +import codechicken.nei.recipe.GuiRecipeButton.UpdateRecipeButtonsEvent; +import codechicken.nei.recipe.Recipe.RecipeId; +import codechicken.nei.util.NEIMouseUtils; public abstract class GuiRecipe extends GuiContainer implements IGuiContainerOverlay, IGuiClientSide, IGuiHandleMouseWheel, IContainerTooltipHandler, INEIGuiHandler { @@ -67,14 +64,14 @@ public abstract class GuiRecipe extends GuiContainer i private static final int BG_BOTTOM_Y = BG_MIDDLE_Y + BG_MIDDLE_HEIGHT; // Button sizes - private static final int borderPadding = 6; - private static final int buttonWidth = 13; - private static final int buttonHeight = 12; + private static final int BORDER_PADDING = 6; + private static final int BUTTON_WIDTH = 13; + private static final int BUTTON_HEIGHT = 12; public static final List recipeFilterers = new LinkedList<>(); protected boolean limitToOneRecipe = false; - protected PermutationTooltipLineHandler permutationTooltipLineHandler; + protected AcceptsFollowingTooltipLineHandler acceptsFollowingTooltipLineHandler; // Background image final DrawableResource bgTop = new DrawableBuilder("nei:textures/gui/recipebg.png", 0, BG_TOP_Y, 176, BG_TOP_HEIGHT) @@ -96,7 +93,7 @@ public abstract class GuiRecipe extends GuiContainer i public int page; public int recipetype; - public BookmarkRecipeId recipeId; + private RecipeId recipeId; public ContainerRecipe slotcontainer; public GuiContainer firstGui; public GuiScreen firstGuiGeneral; @@ -106,8 +103,13 @@ public abstract class GuiRecipe extends GuiContainer i private GuiButton nexttype; private GuiButton prevtype; + // some mods reflect this property + @Deprecated private GuiOverlayButton[] overlayButtons = new GuiOverlayButton[0]; + private final List recipeButtons = new ArrayList<>(); + private String recipePageCacheKey = ""; + private final Rectangle area = new Rectangle(); private final GuiRecipeTabs recipeTabs; private final GuiRecipeCatalyst guiRecipeCatalyst; @@ -116,41 +118,6 @@ public abstract class GuiRecipe extends GuiContainer i private int yShift = 0; - protected static class PermutationTooltipLineHandler extends ItemsTooltipLineHandler { - - protected PositionedStack pStack = null; - - public PermutationTooltipLineHandler(PositionedStack pStack, List items) { - super(NEIClientUtils.translate("recipe.accepts"), items, false, 5); - this.pStack = pStack; - } - - public static PermutationTooltipLineHandler getInstance(PositionedStack pStack) { - final List items = pStack.getFilteredPermutations(); - - if (items.size() > 1) { - return new PermutationTooltipLineHandler(pStack, items); - } - - return null; - } - - @Override - protected void drawItem(int x, int y, ItemStack drawStack, String stackSize) { - - if (StackInfo.equalItemAndNBT(drawStack, this.pStack.item, true)) { - GL11.glPushAttrib(GL11.GL_ALL_ATTRIB_BITS); - GL11.glDisable(GL11.GL_LIGHTING); - GL11.glDisable(GL11.GL_DEPTH_TEST); - GuiDraw.drawRect(x - 1, y - 1, 18, 18, 0x66555555); - GL11.glPopAttrib(); - } - - super.drawItem(x, y, drawStack, stackSize); - } - - } - public static class ItemRecipeFilter implements IRecipeFilter { public ItemFilter filter; @@ -248,7 +215,7 @@ public abstract class GuiRecipe extends GuiContainer i guiRecipe.changePage(0); } else { final IRecipeFilter filter = new ItemRecipeFilter(GuiRecipe.searchField.getFilter()); - List filtered = searchHandler.getSearchResult(filter); + final List filtered = searchHandler.getSearchResult(filter); if (filtered == null) { stop(); @@ -323,9 +290,9 @@ public abstract class GuiRecipe extends GuiContainer i this.firstGui = (GuiContainer) prevgui; } - if (prevgui instanceof IGuiContainerOverlay) { - this.firstGui = ((IGuiContainerOverlay) prevgui).getFirstScreen(); - this.firstGuiGeneral = ((IGuiContainerOverlay) prevgui).getFirstScreenGeneral(); + if (prevgui instanceof IGuiContainerOverlay gui) { + this.firstGui = gui.getFirstScreen(); + this.firstGuiGeneral = gui.getFirstScreenGeneral(); } } @@ -435,23 +402,21 @@ public abstract class GuiRecipe extends GuiContainer i if (handler == null) { setRecipePage(recipetype); - } else { - updateOverlayButtons(); } checkYShift(); - final int rightButtonX = guiLeft + xSize - borderPadding - buttonWidth; - final int leftButtonX = guiLeft + borderPadding; + final int rightButtonX = guiLeft + xSize - BORDER_PADDING - BUTTON_WIDTH; + final int leftButtonX = guiLeft + BORDER_PADDING; - nexttype = new GuiNEIButton(0, leftButtonX, guiTop + 3, buttonWidth, buttonHeight, "<") { + nexttype = new GuiNEIButton(0, leftButtonX, guiTop + 3, BUTTON_WIDTH, BUTTON_HEIGHT, "<") { @Override public void mouseReleased(int mouseX, int mouseY) { prevType(); } }; - prevtype = new GuiNEIButton(1, rightButtonX, guiTop + 3, buttonWidth, buttonHeight, ">") { + prevtype = new GuiNEIButton(1, rightButtonX, guiTop + 3, BUTTON_WIDTH, BUTTON_HEIGHT, ">") { @Override public void mouseReleased(int mouseX, int mouseY) { @@ -459,14 +424,14 @@ public abstract class GuiRecipe extends GuiContainer i } }; - nextpage = new GuiNEIButton(2, leftButtonX, guiTop + 17, buttonWidth, buttonHeight, "<") { + nextpage = new GuiNEIButton(2, leftButtonX, guiTop + 17, BUTTON_WIDTH, BUTTON_HEIGHT, "<") { @Override public void mouseReleased(int mouseX, int mouseY) { prevPage(); } }; - prevpage = new GuiNEIButton(3, rightButtonX, guiTop + 17, buttonWidth, buttonHeight, ">") { + prevpage = new GuiNEIButton(3, rightButtonX, guiTop + 17, BUTTON_WIDTH, BUTTON_HEIGHT, ">") { @Override public void mouseReleased(int mouseX, int mouseY) { @@ -477,7 +442,8 @@ public abstract class GuiRecipe extends GuiContainer i toggleSearch.icon = new DrawableBuilder("nei:textures/nei_sprites.png", 0, 76, 10, 10).build(); this.buttonList.addAll(Arrays.asList(nexttype, prevtype, nextpage, prevpage)); - this.overlayButtons = new GuiOverlayButton[0]; + this.recipeButtons.clear(); + this.recipePageCacheKey = ""; if (currenthandlers.size() == 1) { nexttype.visible = false; @@ -488,22 +454,32 @@ public abstract class GuiRecipe extends GuiContainer i refreshPage(); } - private void updateOverlayButtons() { + private void updateRecipePage() { final List indices = getRecipeIndices(); + final String cacheKey = indices.isEmpty() ? "" + : handlerInfo.getHandlerName() + ":" + indices.get(0) + ":" + indices.get(indices.size() - 1); - if (isDirtyOverlayButtons(indices)) { - this.buttonList.removeIf(Arrays.asList(this.overlayButtons)::contains); + if (!cacheKey.equals(this.recipePageCacheKey)) { + this.recipePageCacheKey = cacheKey; - final int xOffset = (limitToOneRecipe ? 0 : guiLeft) + xSize - borderPadding; - final int yOffset = (limitToOneRecipe ? -buttonHeight : guiTop - 18) + getRecipePosition(0).y; + for (int refIndex = 0; refIndex < indices.size(); refIndex++) { + final int recipeIndex = indices.get(refIndex); + for (PositionedStack pStack : handler.original.getIngredientStacks(recipeIndex)) { + pStack.setPermutationToRender(pStack.getFilteredPermutations().get(0)); + } + } + + final int xOffset = (limitToOneRecipe ? 0 : guiLeft) + xSize - BORDER_PADDING; + final int yOffset = (limitToOneRecipe ? -BUTTON_HEIGHT : guiTop - 18) + getRefIndexPosition(0).y; final int height = handlerInfo.getHeight() - yShift; - final UpdateOverlayButtonsEvent.Pre preEvent = new UpdateOverlayButtonsEvent.Pre( + final boolean showFavorites = NEIClientConfig.favoritesEnabled(); + final UpdateRecipeButtonsEvent.Pre preEvent = new UpdateRecipeButtonsEvent.Pre( this, xOffset, yOffset, height, handlerInfo); - List buttons = new ArrayList<>(); + List buttons = new ArrayList<>(); if (MinecraftForge.EVENT_BUS.post(preEvent)) { buttons = preEvent.buttonList; @@ -517,42 +493,46 @@ public abstract class GuiRecipe extends GuiContainer i continue; } - buttons.add( - new GuiOverlayButton( - firstGui, - handler.original, - recipeIndex, - xOffset - buttonWidth, - yOffset + height * (refIndex + 1), - buttonWidth, - buttonHeight)); + GuiRecipeButton button = new GuiOverlayButton( + firstGui, + RecipeHandlerRef.of(handler.original, recipeIndex), + xOffset - GuiRecipeButton.BUTTON_WIDTH, + yOffset + height * (refIndex + 1)); + + buttons.add(button); + + if (showFavorites) { + buttons.add( + new GuiFavoriteButton( + RecipeHandlerRef.of(handler.original, recipeIndex), + button.xPosition, + button.yPosition - button.height - 1)); + } + } if (this.equals(this.mc.currentScreen)) { - UpdateOverlayButtonsEvent.Post postEvent = new UpdateOverlayButtonsEvent.Post(this, buttons); + UpdateRecipeButtonsEvent.Post postEvent = new UpdateRecipeButtonsEvent.Post(this, buttons); MinecraftForge.EVENT_BUS.post(postEvent); buttons = postEvent.buttonList; } } - this.overlayButtons = buttons.toArray(new GuiOverlayButton[buttons.size()]); + if (!showFavorites) { + buttons.removeIf(GuiFavoriteButton.class::isInstance); + } + + /** compatibility with old format */ + this.overlayButtons = buttons.stream().filter(GuiOverlayButton.class::isInstance) + .toArray(GuiOverlayButton[]::new); + + this.buttonList.removeIf(this.recipeButtons::contains); + this.recipeButtons.clear(); + + this.recipeButtons.addAll(buttons); this.buttonList.addAll(buttons); } - } - private boolean isDirtyOverlayButtons(List indices) { - - if (this.overlayButtons.length == 0) { - return true; - } - - for (GuiOverlayButton button : this.overlayButtons) { - if (button.handler != handler.original || !indices.contains(button.recipeIndex)) { - return true; - } - } - - return false; } public static IRecipeFilter getRecipeListFilter() { @@ -604,11 +584,18 @@ public abstract class GuiRecipe extends GuiContainer i checkYShift(); } - public List getOverlayButtons() { - return Collections.unmodifiableList(Arrays.asList(this.overlayButtons)); + public List getRecipeButtons() { + return Collections.unmodifiableList(this.recipeButtons); } - public int openTargetRecipe(BookmarkRecipeId recipeId) { + public List getOverlayButtons() { + return Collections.unmodifiableList( + Arrays.asList( + this.recipeButtons.stream().filter(GuiOverlayButton.class::isInstance) + .toArray(GuiButton[]::new))); + } + + public int openTargetRecipe(RecipeId recipeId) { int refIndex = -1; int recipetype = 0; @@ -619,13 +606,13 @@ public abstract class GuiRecipe extends GuiContainer i H localHandler = currenthandlers.get(j); HandlerInfo localHandlerInfo = GuiRecipeTab.getHandlerInfo(localHandler); - if (localHandlerInfo.getHandlerName().equals(this.recipeId.handlerName)) { + if (localHandlerInfo.getHandlerName().equals(this.recipeId.getHandleName())) { recipetype = j; - if (!this.recipeId.ingredients.isEmpty()) { + if (!this.recipeId.getIngredients().isEmpty()) { refIndex = SearchRecipeHandler.findFirst( localHandler, - (recipeIndex) -> this.recipeId + recipeIndex -> this.recipeId .equalsIngredients(localHandler.getIngredientStacks(recipeIndex))); } @@ -639,49 +626,21 @@ public abstract class GuiRecipe extends GuiContainer i return refIndex; } - public List getFocusedRecipeIngredients() { - List indices = getRecipeIndices(); + public Recipe getFocusedRecipe() { + final List indices = getRecipeIndices(); for (int refIndex = 0; refIndex < indices.size(); refIndex++) { final int recipeIndex = indices.get(refIndex); if (recipeInFocus(refIndex, recipeIndex)) { - return handler.original.getIngredientStacks(recipeIndex); + return Recipe.of(handler.original, recipeIndex); } } return null; } - public int prepareFocusedRecipeResultStackSize(ItemStack stackover) { - List indices = getRecipeIndices(); - - for (int refIndex = 0; refIndex < indices.size(); refIndex++) { - final int recipeIndex = indices.get(refIndex); - if (recipeInFocus(refIndex, recipeIndex)) { - final PositionedStack result = handler.original.getResultStack(recipeIndex); - int stackSize = 0; - - if (result != null && StackInfo.equalItemAndNBT(result.item, stackover, true)) { - stackSize += result.item.stackSize; - } - - final List stacks = handler.original.getOtherStacks(recipeIndex); - for (PositionedStack pStack : stacks) { - if (StackInfo.equalItemAndNBT(pStack.item, stackover, true)) { - stackSize += pStack.item.stackSize; - } - } - - return stackSize; - } - } - - return stackover.stackSize; - } - protected boolean recipeInFocus(int refIndex, int recipeIndex) { final PositionedStack result = handler.original.getResultStack(recipeIndex); - if (result != null && isMouseOver(result, refIndex)) { return true; } @@ -696,6 +655,15 @@ public abstract class GuiRecipe extends GuiContainer i return false; } + public boolean isMouseOver(PositionedStack stack, int refIndex) { + Point p = getRefIndexPosition(refIndex); + Point mousepos = GuiDraw.getMousePosition(); + Slot stackSlot = slotcontainer.getSlotWithStack(stack, p.x, p.y); + Slot mouseoverSlot = getSlotAtPosition(mousepos.x, mousepos.y); + + return stackSlot == mouseoverSlot; + } + public String getHandlerName() { return handlerInfo.getHandlerName(); } @@ -708,7 +676,7 @@ public abstract class GuiRecipe extends GuiContainer i final int recipesPerPage = getRecipesPerPage(); final int minIndex = page * recipesPerPage; final int maxIndex = Math.min(this.numRecipes(), (page + 1) * recipesPerPage); - final ArrayList range = new ArrayList<>(); + final List range = new ArrayList<>(); for (int index = minIndex; index < maxIndex; index++) { range.add(handler.ref(index)); @@ -752,7 +720,7 @@ public abstract class GuiRecipe extends GuiContainer i Point mouse = GuiDraw.getMousePosition(); - for (GuiOverlayButton button : this.overlayButtons) { + for (GuiRecipeButton button : this.recipeButtons) { if (button.contains(mouse.x, mouse.y)) { button.lastKeyTyped(this, c, i); } @@ -814,9 +782,19 @@ public abstract class GuiRecipe extends GuiContainer i // handler-specific scroll behavior. if (recipeTabs.mouseScrolled(scroll)) return; - for (int recipeIndex : getRecipeIndices()) { - if (handler.original.mouseScrolled(this, scroll, recipeIndex)) { - return; + try (CompatibilityHacks compatibilityHacks = new CompatibilityHacks()) { + Point mouse = GuiDraw.getMousePosition(); + + for (GuiRecipeButton button : this.recipeButtons) { + if (button.contains(mouse.x, mouse.y) && button.mouseScrolled(this, scroll)) { + return; + } + } + + for (int recipeIndex : getRecipeIndices()) { + if (handler.original.mouseScrolled(this, scroll, recipeIndex)) { + return; + } } } @@ -845,8 +823,17 @@ public abstract class GuiRecipe extends GuiContainer i @Override public void updateScreen() { super.updateScreen(); + onUpdate(); + } + + public void onUpdate() { handler.original.onUpdate(); - refreshPage(); + + if (limitToOneRecipe) { + refreshSlots(); + } else { + refreshPage(); + } } @Override @@ -857,7 +844,7 @@ public abstract class GuiRecipe extends GuiContainer i currenttip = handler.original.handleTooltip(this, currenttip, recipeIndex); } - for (GuiOverlayButton button : this.overlayButtons) { + for (GuiRecipeButton button : this.recipeButtons) { if (button.contains(mousex, mousey)) { currenttip = button.handleTooltip(this, currenttip); } @@ -892,36 +879,36 @@ public abstract class GuiRecipe extends GuiContainer i } } - if (NEIClientConfig.showCycledIngredientsTooltip()) { - PositionedStack focused = null; + if (NEIClientConfig.showCycledIngredientsTooltip() && itemstack != null) { + PositionedStack hovered = null; - for (int refIndex = 0; refIndex < indices.size(); refIndex++) { + for (int refIndex = 0; refIndex < indices.size() && hovered == null; refIndex++) { final int recipeIndex = indices.get(refIndex); + final List stacks = handler.original.getIngredientStacks(recipeIndex); - if (itemstack != null && focused == null) { - final List stacks = handler.original.getIngredientStacks(recipeIndex); - - for (PositionedStack pStack : stacks) { - if (isMouseOver(pStack, refIndex)) { - focused = pStack; - break; - } + for (PositionedStack pStack : stacks) { + if (isMouseOver(pStack, refIndex)) { + hovered = pStack; + break; } } } - if (focused == null || focused.items.length <= 1) { - this.permutationTooltipLineHandler = null; - } else if (this.permutationTooltipLineHandler == null - || this.permutationTooltipLineHandler.pStack != focused) { - this.permutationTooltipLineHandler = PermutationTooltipLineHandler.getInstance(focused); + if (hovered == null || hovered.items.length <= 1) { + this.acceptsFollowingTooltipLineHandler = null; + } else if (this.acceptsFollowingTooltipLineHandler == null + || this.acceptsFollowingTooltipLineHandler.tooltipGUID != hovered) { + this.acceptsFollowingTooltipLineHandler = AcceptsFollowingTooltipLineHandler + .of(hovered, hovered.getFilteredPermutations(), hovered.item); } - } else if (this.permutationTooltipLineHandler != null) { - this.permutationTooltipLineHandler = null; + } else if (this.acceptsFollowingTooltipLineHandler != null) { + this.acceptsFollowingTooltipLineHandler = null; } - if (this.permutationTooltipLineHandler != null) { - currenttip.add(GuiDraw.TOOLTIP_HANDLER + GuiDraw.getTipLineId(this.permutationTooltipLineHandler)); + if (this.acceptsFollowingTooltipLineHandler != null) { + this.acceptsFollowingTooltipLineHandler + .setActiveStack(((PositionedStack) this.acceptsFollowingTooltipLineHandler.tooltipGUID).item); + currenttip.add(GuiDraw.TOOLTIP_HANDLER + GuiDraw.getTipLineId(this.acceptsFollowingTooltipLineHandler)); } return currenttip; @@ -931,13 +918,21 @@ public abstract class GuiRecipe extends GuiContainer i public Map handleHotkeys(GuiContainer gui, int mousex, int mousey, Map hotkeys) { try (CompatibilityHacks compatibilityHacks = new CompatibilityHacks()) { - for (GuiOverlayButton button : this.overlayButtons) { + for (GuiRecipeButton button : this.recipeButtons) { if (button.contains(mousex, mousey)) { hotkeys = button.handleHotkeys(gui, mousex, mousey, hotkeys); } } } + if (this.acceptsFollowingTooltipLineHandler != null) { + hotkeys.put( + NEIClientUtils.getKeyName( + NEIClientUtils.SHIFT_HASH, + NEIMouseUtils.MOUSE_BTN_NONE + NEIMouseUtils.MOUSE_SCROLL), + NEIClientUtils.translate("recipe.accepts.scroll")); + } + return hotkeys; } @@ -962,26 +957,6 @@ public abstract class GuiRecipe extends GuiContainer i setRecipePage(--recipetype); } - protected void overlayRecipe(int recipeIndex, boolean shift) { - - if (handler == null || !handler.original.hasOverlay(firstGui, firstGui.inventorySlots, recipeIndex)) { - return; - } - - final boolean requireShiftForOverlayRecipe = NEIClientConfig.requireShiftForOverlayRecipe(); - final IOverlayHandler overlayHandler = handler.original.getOverlayHandler(firstGui, recipeIndex); - final IRecipeOverlayRenderer renderer = handler.original.getOverlayRenderer(firstGui, recipeIndex); - - if (renderer == null || !requireShiftForOverlayRecipe || shift) { - if (overlayHandler != null) { - overlayHandler - .overlayRecipe(firstGui, handler.original, recipeIndex, !requireShiftForOverlayRecipe || shift); - } - } else { - LayoutManager.overlayRenderer = renderer; - } - } - protected void changePage(int shift) { final int recipesPerPage = getRecipesPerPage(); final int numRecipes = this.numRecipes(); @@ -997,7 +972,6 @@ public abstract class GuiRecipe extends GuiContainer i public void refreshPage() { changePage(0); - refreshSlots(); final int recipesPerPage = getRecipesPerPage(); final boolean multiplepages = this.numRecipes() > recipesPerPage; final int numRecipes = Math.min(this.numRecipes() - (page * recipesPerPage), recipesPerPage); @@ -1011,18 +985,19 @@ public abstract class GuiRecipe extends GuiContainer i if (!limitToOneRecipe) { RecipeCatalysts.updatePosition(ySize - BG_TOP_HEIGHT - (GuiRecipeCatalyst.fullBorder * 2)); toggleSearch.w = toggleSearch.h = 12; - toggleSearch.x = borderPadding + buttonWidth; + toggleSearch.x = BORDER_PADDING + BUTTON_WIDTH; toggleSearch.y = 17; searchField.y = 16; - searchField.x = borderPadding + buttonWidth + toggleSearch.w - 1; - searchField.w = xSize - (borderPadding + buttonWidth) * 2 + 1 - toggleSearch.w - 45; + searchField.x = BORDER_PADDING + BUTTON_WIDTH + toggleSearch.w - 1; + searchField.w = xSize - (BORDER_PADDING + BUTTON_WIDTH) * 2 + 1 - toggleSearch.w - 45; searchField.h = 14; } nextpage.enabled = prevpage.enabled = multiplepages; - updateOverlayButtons(); + updateRecipePage(); + refreshSlots(); recipeTabs.refreshPage(); } @@ -1032,7 +1007,7 @@ public abstract class GuiRecipe extends GuiContainer i for (int refIndex = 0; refIndex < indices.size(); refIndex++) { int recipeIndex = indices.get(refIndex); - Point p = getRecipePosition(refIndex); + Point p = getRefIndexPosition(refIndex); // Legacy recipe handlers only expect a single paging widget at the top of the recipe screen, in contrast, // GTNH NEI moves the recipe paging widget from the bottom to the top, which means said legacy handlers will // position item slots 16px too high in the screen. @@ -1042,18 +1017,21 @@ public abstract class GuiRecipe extends GuiContainer i TemplateRecipeHandler.disableCycledIngredients = false; - List stacks = handler.original.getIngredientStacks(recipeIndex); - for (PositionedStack stack : stacks) slotcontainer.addSlot(stack, p.x, p.y); + for (PositionedStack stack : handler.original.getIngredientStacks(recipeIndex)) { + slotcontainer.addSlot(stack, p.x, p.y); + } - stacks = handler.original.getOtherStacks(recipeIndex); - for (PositionedStack stack : stacks) slotcontainer.addSlot(stack, p.x, p.y); + for (PositionedStack stack : handler.original.getOtherStacks(recipeIndex)) { + slotcontainer.addSlot(stack, p.x, p.y); + } PositionedStack result = handler.original.getResultStack(recipeIndex); - if (result != null) slotcontainer.addSlot(result, p.x, p.y); + if (result != null) { + slotcontainer.addSlot(result, p.x, p.y); + } if (!limitToOneRecipe) { - List catalysts = RecipeCatalysts.getRecipeCatalysts(handler.original); - for (PositionedStack catalyst : catalysts) { + for (PositionedStack catalyst : RecipeCatalysts.getRecipeCatalysts(handler.original)) { int xOffset = -GuiRecipeCatalyst.ingredientSize + 1; int yOffset = BG_TOP_Y + GuiRecipeCatalyst.fullBorder; slotcontainer.addSlot(catalyst, xOffset, yOffset); @@ -1108,21 +1086,12 @@ public abstract class GuiRecipe extends GuiContainer i handler.original.drawForeground(recipeIndex); if (limitToOneRecipe) { - GuiOverlayButton overlayButton = Arrays.asList(this.overlayButtons).stream() - .filter(b -> b.recipeIndex == recipeIndex).findAny().orElse(null); + GuiOverlayButton overlayButton = (GuiOverlayButton) this.recipeButtons.stream() + .filter(b -> b instanceof GuiOverlayButton && b.handlerRef.recipeIndex == recipeIndex) + .findAny().orElse(null); if (overlayButton != null && overlayButton.enabled) { - final String overlayKeyName = NEIClientConfig.getKeyName("gui.overlay_use"); - - if (overlayKeyName != null) { - fontRendererObj.drawStringWithShadow( - EnumChatFormatting.ITALIC + overlayKeyName, - xSize - borderPadding - fontRendererObj.getStringWidth(overlayKeyName) - 10, - handlerInfo.getHeight() - fontRendererObj.FONT_HEIGHT, - 0xffffff); - } - - overlayButton.drawItemPresenceOverlay(); + overlayButton.drawItemOverlay(); } } @@ -1134,11 +1103,14 @@ public abstract class GuiRecipe extends GuiContainer i GL11.glPushMatrix(); GL11.glTranslatef(5, ySkip + yShift, 0); - if (!limitToOneRecipe && NEIClientConfig.itemPresenceOverlay() > 0) { - for (GuiOverlayButton button : this.overlayButtons) { + if (!limitToOneRecipe) { + for (GuiRecipeButton button : this.recipeButtons) { if (button.contains(mouseX, mouseY)) { - GL11.glTranslatef(0, handlerInfo.getHeight() * indices.indexOf(button.recipeIndex), 0); - button.drawItemPresenceOverlay(); + GL11.glTranslatef( + 0, + handlerInfo.getHeight() * indices.indexOf(button.handlerRef.recipeIndex), + 0); + button.drawItemOverlay(); break; } } @@ -1158,16 +1130,16 @@ public abstract class GuiRecipe extends GuiContainer i if (NEIClientConfig.areJEIStyleTabsVisible() && !limitToOneRecipe) { drawRect( - guiLeft + borderPadding + buttonWidth - 1, + guiLeft + BORDER_PADDING + BUTTON_WIDTH - 1, nexttype.yPosition, - guiLeft + xSize - borderPadding - buttonWidth, - nexttype.yPosition + buttonHeight, + guiLeft + xSize - BORDER_PADDING - BUTTON_WIDTH, + nexttype.yPosition + BUTTON_HEIGHT, 0x30000000); drawRect( - guiLeft + borderPadding + buttonWidth - 1, + guiLeft + BORDER_PADDING + BUTTON_WIDTH - 1, nextpage.yPosition, - guiLeft + xSize - borderPadding - buttonWidth, - nextpage.yPosition + buttonHeight, + guiLeft + xSize - BORDER_PADDING - BUTTON_WIDTH, + nextpage.yPosition + BUTTON_HEIGHT, 0x30000000); RenderHelper.enableGUIStandardItemLighting(); @@ -1240,15 +1212,6 @@ public abstract class GuiRecipe extends GuiContainer i return firstGuiGeneral; } - public boolean isMouseOver(PositionedStack stack, int refIndex) { - Point p = getRecipePosition(refIndex); - Point mousepos = GuiDraw.getMousePosition(); - Slot stackSlot = slotcontainer.getSlotWithStack(stack, p.x, p.y); - Slot mouseoverSlot = getSlotAtPosition(mousepos.x, mousepos.y); - - return stackSlot == mouseoverSlot; - } - private int getRecipesPerPage() { return limitToOneRecipe ? 1 : getRecipesPerPage(handlerInfo); } @@ -1257,7 +1220,7 @@ public abstract class GuiRecipe extends GuiContainer i if (handlerInfo != null) { return Math.max( Math.min( - ((ySize - (buttonHeight * 3)) / handlerInfo.getHeight()), + ((ySize - (BUTTON_HEIGHT * 3)) / handlerInfo.getHeight()), handlerInfo.getMaxRecipesPerPage()), 1); } else if (handler != null) { @@ -1266,10 +1229,14 @@ public abstract class GuiRecipe extends GuiContainer i return 1; } - public Point getRecipePosition(int refIndex) { - // Legacy recipe handlers using the height hack might use getRecipePosition in combination with guiTop/height to - // position certain elements like tooltips. Since guiTop is moved down by 16px during height hacking, we need to - // reduce the vertical shift here to 16px instead of 32px. + public Point getRecipePosition(int recipe) { + return getRefIndexPosition(getRecipeIndices().indexOf(recipe)); + } + + protected Point getRefIndexPosition(int refIndex) { + // Legacy recipe handlers using the height hack might use getRefIndexPosition in combination with guiTop/height + // to position certain elements like tooltips. Since guiTop is moved down by 16px during height hacking, we need + // to reduce the vertical shift here to 16px instead of 32px. return new Point( 5, (isHeightHackApplied ? 16 : 32) - (limitToOneRecipe ? 25 : 0) @@ -1311,16 +1278,14 @@ public abstract class GuiRecipe extends GuiContainer i return area.intersects(x, y, w, h); } - protected static BookmarkRecipeId getCurrentRecipeId(GuiScreen gui) { + protected static RecipeId getCurrentRecipeId(GuiScreen gui) { - if (gui instanceof GuiRecipe && ((GuiRecipe) gui).handler.numRecipes() > 0) { - final GuiRecipe gRecipe = (GuiRecipe) gui; + if ((gui instanceof GuiRecipegRecipe) && gRecipe.handler.numRecipes() > 0) { final List indices = gRecipe.getRecipeIndices(); final int curRecipe = indices.isEmpty() ? 0 : indices.get(0); + final Recipe recipe = Recipe.of(gRecipe.handler.original, curRecipe); - return new BookmarkRecipeId( - gRecipe.handlerInfo.getHandlerName(), - gRecipe.handler.original.getIngredientStacks(curRecipe)); + return recipe != null ? recipe.getRecipeId() : null; } return null; diff --git a/src/main/java/codechicken/nei/recipe/GuiRecipeButton.java b/src/main/java/codechicken/nei/recipe/GuiRecipeButton.java new file mode 100644 index 00000000..2570e149 --- /dev/null +++ b/src/main/java/codechicken/nei/recipe/GuiRecipeButton.java @@ -0,0 +1,80 @@ +package codechicken.nei.recipe; + +import java.util.ArrayList; +import java.util.List; +import java.util.Map; + +import net.minecraft.client.gui.inventory.GuiContainer; +import net.minecraftforge.client.event.GuiScreenEvent; + +import codechicken.lib.vec.Rectangle4i; +import codechicken.nei.GuiNEIButton; +import cpw.mods.fml.common.eventhandler.Cancelable; + +public abstract class GuiRecipeButton extends GuiNEIButton { + + public static class UpdateRecipeButtonsEvent extends GuiScreenEvent { + + public List buttonList; + + public UpdateRecipeButtonsEvent(GuiRecipe gui, List buttonList) { + super(gui); + this.buttonList = new ArrayList<>(buttonList); + } + + @Cancelable + public static class Pre extends UpdateRecipeButtonsEvent { + + public int xOffset; + public int yOffset; + public int height; + public HandlerInfo handlerInfo; + + public Pre(GuiRecipe gui, int xOffset, int yOffset, int height, HandlerInfo handlerInfo) { + super(gui, new ArrayList<>()); + this.xOffset = xOffset; + this.yOffset = yOffset; + this.height = height; + this.handlerInfo = handlerInfo; + } + } + + public static class Post extends UpdateRecipeButtonsEvent { + + public Post(GuiRecipe gui, List buttonList) { + super(gui, buttonList); + } + } + } + + public static final int BUTTON_WIDTH = 12; + public static final int BUTTON_HEIGHT = 12; + + public final RecipeHandlerRef handlerRef; + + protected GuiRecipeButton(RecipeHandlerRef point, int x, int y, int buttonId, String label) { + super(buttonId, x, y, BUTTON_WIDTH, BUTTON_HEIGHT, label); + this.handlerRef = point; + } + + abstract List handleTooltip(GuiRecipe gui, List currenttip); + + abstract Map handleHotkeys(GuiContainer gui, int mousex, int mousey, Map hotkeys); + + abstract void lastKeyTyped(GuiRecipe gui, char keyChar, int keyID); + + abstract void drawItemOverlay(); + + public boolean mouseScrolled(GuiRecipe gui, int scroll) { + return false; + } + + public Rectangle4i bounds() { + return new Rectangle4i(this.xPosition, this.yPosition, this.width, this.height); + } + + public boolean contains(int mx, int my) { + return bounds().contains(mx, my); + } + +} diff --git a/src/main/java/codechicken/nei/recipe/GuiUsageRecipe.java b/src/main/java/codechicken/nei/recipe/GuiUsageRecipe.java index 6a629d93..366960b9 100644 --- a/src/main/java/codechicken/nei/recipe/GuiUsageRecipe.java +++ b/src/main/java/codechicken/nei/recipe/GuiUsageRecipe.java @@ -10,6 +10,7 @@ import net.minecraft.item.ItemStack; import codechicken.nei.NEIClientConfig; import codechicken.nei.NEIClientUtils; +import codechicken.nei.recipe.Recipe.RecipeId; import codechicken.nei.recipe.stackinfo.GTFluidStackStringifyHandler; public class GuiUsageRecipe extends GuiRecipe { @@ -30,7 +31,7 @@ public class GuiUsageRecipe extends GuiRecipe { if (!handlers.isEmpty()) { final Minecraft mc = NEIClientUtils.mc(); - final BookmarkRecipeId recipeId = getCurrentRecipeId(mc.currentScreen); + final RecipeId recipeId = getCurrentRecipeId(mc.currentScreen); final GuiUsageRecipe gui = new GuiUsageRecipe(handlers); mc.displayGuiScreen(gui); diff --git a/src/main/java/codechicken/nei/recipe/Recipe.java b/src/main/java/codechicken/nei/recipe/Recipe.java new file mode 100644 index 00000000..9832de85 --- /dev/null +++ b/src/main/java/codechicken/nei/recipe/Recipe.java @@ -0,0 +1,456 @@ +package codechicken.nei.recipe; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; +import java.util.stream.Collectors; + +import net.minecraft.item.ItemStack; +import net.minecraft.nbt.NBTTagCompound; +import net.minecraftforge.fluids.FluidContainerRegistry; + +import com.google.common.base.Objects; +import com.google.gson.JsonArray; +import com.google.gson.JsonElement; +import com.google.gson.JsonObject; +import com.google.gson.JsonPrimitive; + +import codechicken.nei.NEIServerUtils; +import codechicken.nei.PositionedStack; +import codechicken.nei.api.ItemInfo; +import codechicken.nei.util.NBTJson; + +public class Recipe { + + public static class RecipeId { + + private static final String KEY_HANDLER_NAME = "handlerName"; + private static final String KEY_INGREDIENTS = "ingredients"; + private static final String KEY_RESULT = "result"; + + private final String handlerName; + private final List ingredients; + private final ItemStack result; + + protected RecipeId(ItemStack result, String handlerName, List ingredients) { + this.result = result != null ? StackInfo.withAmount(result, 0) : null; + this.handlerName = handlerName; + this.ingredients = ingredients; + } + + public static RecipeId of(Object result, String handlerName, Iterable ingredients) { + return new RecipeId(extractItem(result), handlerName, extractItems(ingredients)); + } + + public static RecipeId of(JsonObject json) { + String handlerName = null; + List ingredients = new ArrayList<>(); + ItemStack result = null; + + if (json.get(KEY_HANDLER_NAME) != null) { + handlerName = json.get(KEY_HANDLER_NAME).getAsString(); + } + + if (json.get(KEY_INGREDIENTS) != null) { + ingredients = convertJsonArrayToItems(json.getAsJsonArray(KEY_INGREDIENTS)); + } + + if (json.get(KEY_RESULT) != null) { + result = StackInfo.loadFromNBT((NBTTagCompound) NBTJson.toNbt(json.getAsJsonObject(KEY_RESULT))); + } + + return new RecipeId(result, handlerName, ingredients); + } + + public String getHandleName() { + return this.handlerName; + } + + public ItemStack getResult() { + return this.result; + } + + public List getIngredients() { + return new ArrayList<>(this.ingredients); + } + + protected static List extractItems(Iterable items) { + final List list = new ArrayList<>(); + + for (Object item : items) { + ItemStack stack = extractItem(item); + + if (stack != null) { + list.add(stack.copy()); + } + } + + return list; + } + + protected static ItemStack extractItem(Object item) { + + if (item instanceof PositionedStack positionedStack) { + item = StackInfo.getItemStackWithMinimumDamage(positionedStack.items); + } + + if (item instanceof RecipeIngredient ingr) { + item = StackInfo.getItemStackWithMinimumDamage(ingr.getPermutations().toArray(new ItemStack[0])); + } + + if (item instanceof NBTTagCompound nbTag) { + return StackInfo.loadFromNBT(nbTag); + } + + if (item instanceof ItemStack stack) { + return stack; + } + + return null; + } + + public boolean equalsIngredients(List stacks) { + + if (ingredients.size() != stacks.size()) { + return false; + } + + for (int index = 0; index < stacks.size(); index++) { + if (!stacks.get(index).containsWithNBT(ingredients.get(index))) { + return false; + } + } + + return true; + } + + public JsonObject toJsonObject() { + final JsonObject json = new JsonObject(); + + if (this.handlerName != null && !"".equals(this.handlerName)) { + json.add(KEY_HANDLER_NAME, new JsonPrimitive(this.handlerName)); + json.add(KEY_INGREDIENTS, convertItemsToJsonArray(this.ingredients)); + + if (this.result != null) { // old format without result + json.add(KEY_RESULT, NBTJson.toJsonObject(StackInfo.itemStackToNBT(this.result))); + } + } + + return json; + } + + protected JsonArray convertItemsToJsonArray(List items) { + final JsonArray arr = new JsonArray(); + + for (ItemStack nbTag : items) { + arr.add(NBTJson.toJsonObject(StackInfo.itemStackToNBT(nbTag))); + } + + return arr; + } + + protected static List convertJsonArrayToItems(JsonArray arr) { + final List items = new ArrayList<>(); + + for (JsonElement elem : arr) { + final ItemStack nbt = StackInfo.loadFromNBT((NBTTagCompound) NBTJson.toNbt(elem)); + + if (nbt == null) { + return new ArrayList<>(); + } + + items.add(nbt); + } + + return items; + } + + @Override + public int hashCode() { + return Objects.hashCode(this.handlerName, this.ingredients.size()); + } + + @Override + public boolean equals(Object anObject) { + if (this == anObject) { + return true; + } + + if (anObject instanceof RecipeId anRecipeId) { + + if (!this.handlerName.equals(anRecipeId.handlerName)) { + return false; + } + + if (this.ingredients.size() != anRecipeId.ingredients.size()) { + return false; + } + + if (this.result != null && anRecipeId.result != null + && !StackInfo.equalItemAndNBT(this.result, anRecipeId.result, true)) { + return false; + } + + for (int idx = 0; idx < this.ingredients.size(); idx++) { + if (!StackInfo.equalItemAndNBT(this.ingredients.get(idx), anRecipeId.ingredients.get(idx), true)) { + return false; + } + } + + return true; + } + + return false; + } + + public RecipeId copy() { + return new RecipeId(this.result, this.handlerName, this.ingredients); + } + + } + + public static class RecipeIngredient { + + protected final ItemStack[] items; + protected int activeIndex = 0; + protected int amount = 0; + + protected int relx; + protected int rely; + + public RecipeIngredient(int relx, int rely, List items, int activeIndex) { + this.items = items.stream().map(ItemStack::copy).toArray(size -> new ItemStack[size]); + this.amount = StackInfo.getAmount(this.items[activeIndex]); + this.activeIndex = activeIndex; + this.relx = relx; + this.rely = rely; + } + + public static RecipeIngredient of(PositionedStack positionedStack) { + final List stacks = Arrays.asList(positionedStack.items).stream() + .filter(item -> !ItemInfo.isHidden(item)).collect(Collectors.toList()); + int activeIndex = 0; + + if (stacks.isEmpty()) { + stacks.addAll(Arrays.asList(positionedStack.items)); + } + + for (int i = 0; i < stacks.size(); i++) { + if (NEIServerUtils.areStacksSameTypeCraftingWithNBT(positionedStack.item, stacks.get(i))) { + activeIndex = i; + break; + } + } + + return of(positionedStack.relx, positionedStack.rely, stacks, activeIndex); + } + + public static RecipeIngredient of(int relx, int rely, List stacks, int activeIndex) { + return new RecipeIngredient(relx, rely, stacks, activeIndex); + } + + public int getAmount() { + return this.amount; + } + + public ItemStack getItemStack() { + return this.items[this.activeIndex]; + } + + public void setActiveIndex(int activeIndex) { + this.activeIndex = Math.max(0, Math.min(activeIndex, this.items.length - 1)); + } + + public boolean contains(ItemStack stackA) { + return getPermutations().stream().anyMatch(stackB -> StackInfo.equalItemAndNBT(stackA, stackB, true)); + } + + public List getPermutations() { + return Arrays.asList(this.items); + } + + public RecipeIngredient setAmount(int amount) { + + for (int index = 0; index < this.items.length; index++) { + this.items[index] = StackInfo.withAmount(this.items[index], amount); + } + + return this; + } + + public RecipeIngredient copy() { + return new RecipeIngredient( + this.relx, + this.rely, + getPermutations().stream().map(ItemStack::copy).collect(Collectors.toList()), + this.activeIndex); + } + } + + private final String handlerName; + private final RecipeIngredient[] results; + private final RecipeIngredient[] ingredients; + private RecipeId recipeId = null; + + private Recipe(String handlerName, List ingredients, List results) { + this.handlerName = handlerName; + this.ingredients = ingredients.toArray(new RecipeIngredient[ingredients.size()]); + this.results = results.toArray(new RecipeIngredient[results.size()]); + } + + public static Recipe of(IRecipeHandler handler, int recipeIndex) { + final String handlerName = GuiRecipeTab.getHandlerInfo(handler).getHandlerName(); + final List ingredients = new ArrayList<>(); + final List results = new ArrayList<>(); + + for (PositionedStack positionedStack : handler.getIngredientStacks(recipeIndex)) { + ingredients.add(RecipeIngredient.of(positionedStack)); + } + + if (handler.getResultStack(recipeIndex) != null) { + results.add(RecipeIngredient.of(handler.getResultStack(recipeIndex))); + } else { + for (PositionedStack positionedStack : handler.getOtherStacks(recipeIndex)) { + results.add(RecipeIngredient.of(positionedStack)); + } + } + + return new Recipe(handlerName, ingredients, results); + } + + public static Recipe of(List results, String handlerName, List ingredients) { + return new Recipe(handlerName, extractItems(ingredients), extractItems(results)); + } + + public static Recipe of(RecipeHandlerRef handlerRef) { + return Recipe.of(handlerRef.handler, handlerRef.recipeIndex); + } + + public static Recipe of(RecipeId recipeId) { + if (recipeId == null) { + return null; + } + + final RecipeHandlerRef handlerRef = RecipeHandlerRef.of(recipeId); + + if (handlerRef != null) { + return Recipe.of(handlerRef); + } + + return null; + } + + protected static List extractItems(List items) { + final List list = new ArrayList<>(); + + for (Object item : items) { + RecipeIngredient ingr = extractItem(item); + + if (ingr != null) { + list.add(ingr); + } + } + + return list; + } + + protected static RecipeIngredient extractItem(Object item) { + + if (item instanceof PositionedStack positionedStack) { + return RecipeIngredient.of(positionedStack); + } + + if (item instanceof NBTTagCompound nbTag) { + item = StackInfo.loadFromNBT(nbTag); + } + + if (item instanceof ItemStack stack) { + return RecipeIngredient.of(0, 0, Arrays.asList(stack), 0); + } + + if (item instanceof RecipeIngredient ingr) { + return ingr.copy(); + } + + return null; + } + + public String getHandlerName() { + return this.handlerName; + } + + public List getResults() { + return Arrays.asList(this.results); + } + + public List getIngredients() { + return Arrays.asList(this.ingredients); + } + + public ItemStack getResult() { + return getResult(null); + } + + public ItemStack getResult(ItemStack stack) { + + if (this.results.length == 0) { + return null; + } + + if (stack == null && this.results.length > 1) { + for (RecipeIngredient ingr : this.results) { + if (!FluidContainerRegistry.isContainer(ingr.getItemStack()) + || StackInfo.getFluid(ingr.getItemStack()) != null) { + stack = ingr.getItemStack(); + break; + } + } + } + + if (stack == null) { + stack = this.results[0].getItemStack(); + } + + int stackSize = 0; + + for (RecipeIngredient result : this.results) { + if (result.contains(stack)) { + stackSize += result.getAmount(); + } + } + + return StackInfo.withAmount(stack, stackSize); + } + + public RecipeId getRecipeId() { + + if (this.recipeId == null) { + this.recipeId = RecipeId.of(getResult(), this.handlerName, getIngredients()); + } + + return this.recipeId; + } + + public void setCustomRecipeId(RecipeId recipeId) { + this.recipeId = recipeId; + } + + public Recipe copy() { + final List ingredients = new ArrayList<>(); + final List results = new ArrayList<>(); + + for (RecipeIngredient ingr : this.ingredients) { + ingredients.add(ingr.copy()); + } + + for (RecipeIngredient res : this.results) { + results.add(res.copy()); + } + + Recipe recipe = new Recipe(this.handlerName, ingredients, results); + recipe.recipeId = this.recipeId; + + return recipe; + } + +} diff --git a/src/main/java/codechicken/nei/recipe/RecipeHandlerRef.java b/src/main/java/codechicken/nei/recipe/RecipeHandlerRef.java new file mode 100644 index 00000000..3255f7a0 --- /dev/null +++ b/src/main/java/codechicken/nei/recipe/RecipeHandlerRef.java @@ -0,0 +1,185 @@ +package codechicken.nei.recipe; + +import java.util.Collections; +import java.util.List; + +import net.minecraft.client.gui.inventory.GuiContainer; + +import codechicken.nei.LRUCache; +import codechicken.nei.LayoutManager; +import codechicken.nei.api.IOverlayHandler; +import codechicken.nei.api.IRecipeOverlayRenderer; +import codechicken.nei.recipe.GuiOverlayButton.ItemOverlayState; +import codechicken.nei.recipe.Recipe.RecipeId; + +public class RecipeHandlerRef { + + private static final LRUCache recipeRefCache = new LRUCache<>(200); + public final IRecipeHandler handler; + public final int recipeIndex; + + private RecipeHandlerRef(IRecipeHandler handler, int recipeIndex) { + this.handler = handler; + this.recipeIndex = recipeIndex; + } + + public static RecipeHandlerRef of(IRecipeHandler handler, int recipeIndex) { + return new RecipeHandlerRef(handler, recipeIndex); + } + + public static RecipeHandlerRef of(RecipeId recipeId) { + + if (recipeId.getResult() != null && !recipeId.getIngredients().isEmpty()) { + return recipeRefCache.computeIfAbsent(recipeId, ri -> { + final List handlers = GuiCraftingRecipe + .getCraftingHandlers("recipeId", recipeId.getResult(), recipeId); + + for (ICraftingHandler handler : handlers) { + int refIndex = SearchRecipeHandler.findFirst( + handler, + recipeIndex -> recipeId.equalsIngredients(handler.getIngredientStacks(recipeIndex))); + + if (refIndex >= 0) { + return new RecipeHandlerRef(handler, refIndex); + } + } + + return null; + }); + } + + return null; + } + + private GuiContainer prepareGui(GuiContainer firstGui) { + + if (firstGui instanceof GuiRecipe gui) { + firstGui = gui.firstGui; + } + + return firstGui != null && firstGui.inventorySlots != null ? firstGui : null; + } + + public IOverlayHandler getOverlayHandler(GuiContainer gui) { + final GuiContainer firstGui = prepareGui(gui); + + if (firstGui != null && this.handler.hasOverlay(firstGui, firstGui.inventorySlots, this.recipeIndex)) { + return this.handler.getOverlayHandler(firstGui, this.recipeIndex); + } + + return null; + } + + public boolean canFillCraftingGrid(GuiContainer gui) { + final IOverlayHandler overlayHandler = getOverlayHandler(gui); + + if (overlayHandler != null) { + return overlayHandler.canFillCraftingGrid(prepareGui(gui), this.handler, this.recipeIndex); + } + + return false; + } + + public boolean canCraft(GuiContainer gui) { + final IOverlayHandler overlayHandler = getOverlayHandler(gui); + + if (overlayHandler != null && allowedTransferAlghoritm(gui)) { + return overlayHandler.canCraft(prepareGui(gui), this.handler, this.recipeIndex); + } + + return false; + } + + public List getPresenceOverlay(GuiContainer gui) { + final IOverlayHandler overlayHandler = getOverlayHandler(gui); + + if (overlayHandler != null) { + return overlayHandler.presenceOverlay(prepareGui(gui), this.handler, this.recipeIndex); + } + + return Collections.emptyList(); + } + + public void fillCraftingGrid(GuiContainer gui, int multiplier) { + final IOverlayHandler overlayHandler = getOverlayHandler(gui); + + if (overlayHandler != null) { + final GuiContainer firstGui = prepareGui(gui); + + if (!firstGui.equals(firstGui.mc.currentScreen)) { + firstGui.mc.displayGuiScreen(firstGui); + } + + if (!allowedTransferAlghoritm(gui)) { + overlayHandler.overlayRecipe(firstGui, this.handler, this.recipeIndex, multiplier != 1); + } else { + overlayHandler.transferRecipe(firstGui, this.handler, this.recipeIndex, multiplier); + } + + } + + } + + public boolean craft(GuiContainer gui, int multiplier) { + final GuiContainer firstGui = prepareGui(gui); + final IOverlayHandler overlayHandler = getOverlayHandler(firstGui); + + if (overlayHandler != null && overlayHandler.canCraft(firstGui, this.handler, this.recipeIndex)) { + + if (!firstGui.equals(firstGui.mc.currentScreen)) { + firstGui.mc.displayGuiScreen(firstGui); + } + + return overlayHandler.craft(firstGui, this.handler, this.recipeIndex, multiplier); + } + + return false; + } + + public boolean allowedTransferAlghoritm(GuiContainer gui) { + final IOverlayHandler overlayHandler = getOverlayHandler(gui); + + if (overlayHandler != null) { + try { + return !overlayHandler.getClass() + .getDeclaredMethod( + "transferRecipe", + GuiContainer.class, + IRecipeHandler.class, + int.class, + int.class) + .isDefault(); + } catch (NoSuchMethodException e) {} + } + + return false; + } + + public IRecipeOverlayRenderer getRecipeOverlayRenderer(GuiContainer gui) { + final GuiContainer firstGui = prepareGui(gui); + + if (firstGui != null && this.handler.hasOverlay(firstGui, firstGui.inventorySlots, this.recipeIndex)) { + return this.handler.getOverlayRenderer(firstGui, this.recipeIndex); + } + + return null; + } + + public boolean canUseOverlayRenderer(GuiContainer gui) { + return getRecipeOverlayRenderer(gui) != null; + } + + public void useOverlayRenderer(GuiContainer gui) { + final IRecipeOverlayRenderer renderer = getRecipeOverlayRenderer(gui); + + if (renderer != null) { + final GuiContainer firstGui = prepareGui(gui); + + if (!firstGui.equals(firstGui.mc.currentScreen)) { + firstGui.mc.displayGuiScreen(firstGui); + } + + LayoutManager.overlayRenderer = renderer; + } + } +} diff --git a/src/main/java/codechicken/nei/recipe/RecipeItemInputHandler.java b/src/main/java/codechicken/nei/recipe/RecipeItemInputHandler.java index 61a0d192..5072537c 100644 --- a/src/main/java/codechicken/nei/recipe/RecipeItemInputHandler.java +++ b/src/main/java/codechicken/nei/recipe/RecipeItemInputHandler.java @@ -1,17 +1,28 @@ package codechicken.nei.recipe; +import java.util.List; import java.util.Map; import net.minecraft.client.gui.inventory.GuiContainer; +import net.minecraft.item.ItemStack; +import codechicken.lib.gui.GuiDraw; +import codechicken.lib.vec.Rectangle4i; +import codechicken.nei.BookmarkPanel.BookmarkViewMode; +import codechicken.nei.FavoriteRecipes; import codechicken.nei.ItemPanels; +import codechicken.nei.NEIClientConfig; import codechicken.nei.api.ShortcutInputHandler; +import codechicken.nei.bookmark.BookmarksGridSlot; import codechicken.nei.guihook.GuiContainerManager; import codechicken.nei.guihook.IContainerInputHandler; import codechicken.nei.guihook.IContainerTooltipHandler; +import codechicken.nei.recipe.Recipe.RecipeId; public class RecipeItemInputHandler implements IContainerInputHandler, IContainerTooltipHandler { + protected RecipeTooltipLineHandler recipeTooltipLineHandler = null; + public static void load() { RecipeItemInputHandler recipeHandler = new RecipeItemInputHandler(); GuiContainerManager.addInputHandler(recipeHandler); @@ -26,7 +37,8 @@ public class RecipeItemInputHandler implements IContainerInputHandler, IContaine @Override public boolean mouseClicked(GuiContainer gui, int mousex, int mousey, int button) { if (!(gui instanceof GuiRecipe) || ItemPanels.itemPanel.contains(mousex, mousey) - || ItemPanels.bookmarkPanel.contains(mousex, mousey)) + || ItemPanels.bookmarkPanel.contains(mousex, mousey) + || ItemPanels.itemPanel.historyPanel.contains(mousex, mousey)) return false; return ShortcutInputHandler.handleMouseClick(GuiContainerManager.getStackMouseOver(gui)); @@ -46,6 +58,63 @@ public class RecipeItemInputHandler implements IContainerInputHandler, IContaine return hotkeys; } + @Override + public List handleItemTooltip(GuiContainer gui, ItemStack itemstack, int mousex, int mousey, + List currenttip) { + + if (itemstack != null) { + final RecipeId recipeId = getHoveredRecipeId(gui, itemstack, mousex, mousey); + + if (recipeId == null) { + recipeTooltipLineHandler = null; + } else if (recipeTooltipLineHandler == null || !recipeTooltipLineHandler.getRecipeId().equals(recipeId)) { + recipeTooltipLineHandler = new RecipeTooltipLineHandler(recipeId); + } + + if (recipeTooltipLineHandler != null) { + currenttip.add(GuiDraw.TOOLTIP_HANDLER + GuiDraw.getTipLineId(recipeTooltipLineHandler)); + } + } else { + recipeTooltipLineHandler = null; + } + + return currenttip; + } + + private RecipeId getHoveredRecipeId(GuiContainer gui, ItemStack itemstack, int mousex, int mousey) { + + if (ItemPanels.bookmarkPanel.contains(mousex, mousey)) { + final BookmarksGridSlot slot = ItemPanels.bookmarkPanel.getSlotMouseOver(mousex, mousey); + + if (slot != null && slot.getRecipeId() != null + && !slot.isIngredient() + && NEIClientConfig.getRecipeTooltipsMode() != 0) { + final int tooltipMode = NEIClientConfig.getRecipeTooltipsMode(); + final BookmarkViewMode viewMode = slot.getGroup().viewMode; + if (tooltipMode == 3 || viewMode == BookmarkViewMode.DEFAULT && tooltipMode == 1 + || viewMode == BookmarkViewMode.TODO_LIST && tooltipMode == 2) { + return slot.getRecipeId(); + } + } + + return null; + } + + if (ItemPanels.itemPanel.contains(mousex, mousey) + || ItemPanels.itemPanel.historyPanel.contains(mousex, mousey)) { + return NEIClientConfig.showRecipeTooltipInPanel() ? FavoriteRecipes.getFavorite(itemstack) : null; + } + + if (gui instanceof GuiRecipeguiRecipe + && new Rectangle4i(gui.guiLeft, gui.guiTop, gui.xSize, gui.ySize).contains(mousex, mousey) + && NEIClientConfig.showRecipeTooltipInGui() + && guiRecipe.getFocusedRecipe() == null) { + return FavoriteRecipes.getFavorite(itemstack); + } + + return null; + } + @Override public void onKeyTyped(GuiContainer gui, char keyChar, int keyID) {} diff --git a/src/main/java/codechicken/nei/recipe/RecipeTooltipLineHandler.java b/src/main/java/codechicken/nei/recipe/RecipeTooltipLineHandler.java new file mode 100644 index 00000000..918c3e50 --- /dev/null +++ b/src/main/java/codechicken/nei/recipe/RecipeTooltipLineHandler.java @@ -0,0 +1,96 @@ +package codechicken.nei.recipe; + +import java.awt.Dimension; +import java.util.List; + +import net.minecraft.client.renderer.RenderHelper; +import net.minecraft.inventory.Slot; +import net.minecraft.item.ItemStack; + +import org.lwjgl.opengl.GL11; +import org.lwjgl.opengl.GL12; + +import codechicken.lib.gui.GuiDraw.ITooltipLineHandler; +import codechicken.nei.guihook.GuiContainerManager; +import codechicken.nei.recipe.Recipe.RecipeId; + +public class RecipeTooltipLineHandler implements ITooltipLineHandler { + + protected long lastUpdate = System.currentTimeMillis(); + protected GuiRecipe gui = null; + protected RecipeId recipeId = null; + protected boolean createdGui = false; + + public RecipeTooltipLineHandler(RecipeId recipeId) { + this.recipeId = recipeId; + } + + public ItemStack getItemStack() { + return this.recipeId.getResult(); + } + + public RecipeId getRecipeId() { + return this.recipeId; + } + + @Override + public Dimension getSize() { + + if (this.gui == null && !this.createdGui) { + this.gui = GuiCraftingRecipe.createRecipeGui("recipeId", false, this.recipeId.getResult(), this.recipeId); + this.createdGui = true; + + if (this.gui != null) { + this.gui.initGui(); + this.gui.guiTop = 0; + this.gui.guiLeft = 0; + } + } + + if (this.gui != null) { + return this.gui.getWidgetSize(); + } + + return new Dimension(0, 0); + } + + @Override + public void draw(int x, int y) { + if (this.gui == null) return; + + if ((System.currentTimeMillis() - lastUpdate) > 50) { + lastUpdate = System.currentTimeMillis(); + this.gui.onUpdate(); + } + + GL11.glPushMatrix(); + GL11.glScaled(1, 1, 3); + GL11.glTranslatef(x, y, 0); + GL11.glPushAttrib(GL11.GL_ALL_ATTRIB_BITS); + RenderHelper.disableStandardItemLighting(); + + this.gui.drawGuiContainerBackgroundLayer(0.0f, -100, -100); + + GL11.glPopAttrib(); + + if (this.gui.slotcontainer != null) { + GL11.glPushAttrib(GL11.GL_ALL_ATTRIB_BITS); + RenderHelper.enableGUIStandardItemLighting(); + GL11.glEnable(GL12.GL_RESCALE_NORMAL); + GL11.glDisable(GL11.GL_DEPTH_TEST); + List slots = this.gui.slotcontainer.inventorySlots; + + for (Slot slot : slots) { + if (slot != null && slot.getStack() != null) { + GuiContainerManager.drawItem(slot.xDisplayPosition, slot.yDisplayPosition, slot.getStack()); + } + } + + GL11.glPopAttrib(); + } + + this.gui.drawGuiContainerForegroundLayer(-100, -100); + + GL11.glPopMatrix(); + } +} diff --git a/src/main/java/codechicken/nei/recipe/StackInfo.java b/src/main/java/codechicken/nei/recipe/StackInfo.java index 81733323..aed47ee1 100644 --- a/src/main/java/codechicken/nei/recipe/StackInfo.java +++ b/src/main/java/codechicken/nei/recipe/StackInfo.java @@ -90,7 +90,7 @@ public class StackInfo { return false; } - if (useNBT) { + if (useNBT && stackA != stackB) { NBTTagCompound tagCompoundA = itemStackToNBT(stackA, false); NBTTagCompound tagCompoundB = itemStackToNBT(stackB, false); diff --git a/src/main/java/codechicken/nei/recipe/TemplateRecipeHandler.java b/src/main/java/codechicken/nei/recipe/TemplateRecipeHandler.java index e8156c02..be28aacb 100644 --- a/src/main/java/codechicken/nei/recipe/TemplateRecipeHandler.java +++ b/src/main/java/codechicken/nei/recipe/TemplateRecipeHandler.java @@ -31,6 +31,7 @@ import com.google.common.base.Stopwatch; import codechicken.lib.gui.GuiDraw; import codechicken.lib.vec.Rectangle4i; +import codechicken.nei.FavoriteRecipes; import codechicken.nei.ItemList; import codechicken.nei.NEIClientConfig; import codechicken.nei.NEIClientUtils; @@ -193,7 +194,7 @@ public abstract class TemplateRecipeHandler implements ICraftingHandler, IUsageH for (PositionedStack pStack : stacks) { if (pStack.items.length <= 1) continue; - final List items = pStack.getFilteredPermutations(); + final List items = pStack.getFilteredPermutations(FavoriteRecipes::contains); pStack.setPermutationToRender(items.get((int) (cycle % items.size()))); } } @@ -204,7 +205,7 @@ public abstract class TemplateRecipeHandler implements ICraftingHandler, IUsageH : Math.abs(new Random(cycle + offset).nextInt()); if (stack.items.length <= 1) return; - final List items = stack.getFilteredPermutations(); + final List items = stack.getFilteredPermutations(FavoriteRecipes::contains); stack.setPermutationToRender(items.get((int) (cycle % items.size()))); } @@ -706,15 +707,7 @@ public abstract class TemplateRecipeHandler implements ICraftingHandler, IUsageH if (overStack != null && overStack.items.length > 1) { final List items = overStack.getFilteredPermutations(); - int stackIndex = 0; - - for (int index = 0; index < items.size(); index++) { - if (NEIServerUtils.areStacksSameTypeCraftingWithNBT(items.get(index), overStack.item)) { - stackIndex = index; - break; - } - } - + final int stackIndex = getPermutationIndex(overStack.item, items); final ItemStack stack = items.get((items.size() - scroll + stackIndex) % items.size()); if (NEIClientConfig.useJEIStyledCycledIngredients()) { @@ -733,6 +726,17 @@ public abstract class TemplateRecipeHandler implements ICraftingHandler, IUsageH return false; } + private int getPermutationIndex(ItemStack stack, List items) { + + for (int index = 0; index < items.size(); index++) { + if (NEIServerUtils.areStacksSameTypeCraftingWithNBT(items.get(index), stack)) { + return index; + } + } + + return -1; + } + private PositionedStack getIngredientMouseOver(int mousex, int mousey, int recipe) { for (PositionedStack pStack : getIngredientStacks(recipe)) { diff --git a/src/main/java/codechicken/nei/recipe/chain/RecipeChainIterator.java b/src/main/java/codechicken/nei/recipe/chain/RecipeChainIterator.java new file mode 100644 index 00000000..960ae336 --- /dev/null +++ b/src/main/java/codechicken/nei/recipe/chain/RecipeChainIterator.java @@ -0,0 +1,103 @@ +package codechicken.nei.recipe.chain; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.HashMap; +import java.util.HashSet; +import java.util.Iterator; +import java.util.List; +import java.util.Map; +import java.util.Set; + +import net.minecraft.item.ItemStack; + +import codechicken.nei.ItemStackAmount; +import codechicken.nei.bookmark.BookmarkItem; +import codechicken.nei.recipe.Recipe.RecipeId; +import codechicken.nei.recipe.StackInfo; + +public class RecipeChainIterator implements Iterator> { + + public final ItemStackAmount inventory = new ItemStackAmount(); + private final Set precessedRecipes = new HashSet<>(); + private RecipeChainMath math; + private Set topRecipes; + private Set processedRecipes; + private List initialItems; + + public RecipeChainIterator(RecipeChainMath math, List initialItems) { + this.math = math; + this.topRecipes = this.math.outputRecipes.keySet(); + this.initialItems = new ArrayList<>(initialItems); + this.processedRecipes = new HashSet<>(this.topRecipes); + } + + public void updateInventory(ItemStack[] mainInventory) { + updateInventory(ItemStackAmount.of(Arrays.asList(mainInventory))); + } + + public void updateInventory(ItemStackAmount mainInventory) { + this.inventory.clear(); + this.inventory.putAll(mainInventory); + } + + @Override + public Map next() { + refreshInitialItems(this.math, this.initialItems); + this.math.refresh(); + + final Set skipRecipes = new HashSet<>(); + final Map rootRecipes = new HashMap<>(); + final HashMap preferredItems = new HashMap<>(this.math.preferredItems); + preferredItems.values() + .removeIf(item -> item == null || item.amount == 0 || this.precessedRecipes.contains(item.recipeId)); + preferredItems.keySet().removeIf(item -> item.amount == 0); + + for (Map.Entry entry : preferredItems.entrySet()) { + final BookmarkItem keyItem = entry.getKey(); + if (this.topRecipes.contains(keyItem.recipeId) && !skipRecipes.contains(keyItem.recipeId)) { + final BookmarkItem item = entry.getValue(); + + if (preferredItems.entrySet().stream().anyMatch( + pref -> !this.processedRecipes.contains(pref.getKey().recipeId) + && item.equals(pref.getValue()))) { + skipRecipes.add(item.recipeId); + } else { + rootRecipes.put( + item.recipeId, + Math.max(rootRecipes.getOrDefault(item.recipeId, 0L), item.getMultiplier())); + } + } + } + + for (RecipeId recipeId : skipRecipes) { + rootRecipes.remove(recipeId); + } + + this.processedRecipes.addAll(rootRecipes.keySet()); + rootRecipes.values().removeIf(amount -> amount == 0); + this.topRecipes = rootRecipes.keySet(); + this.precessedRecipes.addAll(this.topRecipes); + + return rootRecipes; + } + + @Override + public boolean hasNext() { + return !this.topRecipes.isEmpty(); + } + + private void refreshInitialItems(RecipeChainMath math, List initialItems) { + math.initialItems.clear(); + math.initialItems.addAll(initialItems); + + for (ItemStack stack : this.inventory.values()) { + final long invStackSize = this.inventory.getOrDefault(stack, 0L); + + if (invStackSize > 0) { + math.initialItems.add(BookmarkItem.of(-1, StackInfo.withAmount(stack, invStackSize))); + } + } + } + +} diff --git a/src/main/java/codechicken/nei/recipe/chain/RecipeChainMath.java b/src/main/java/codechicken/nei/recipe/chain/RecipeChainMath.java new file mode 100644 index 00000000..aec1fb81 --- /dev/null +++ b/src/main/java/codechicken/nei/recipe/chain/RecipeChainMath.java @@ -0,0 +1,296 @@ +package codechicken.nei.recipe.chain; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.HashMap; +import java.util.HashSet; +import java.util.List; +import java.util.Map; +import java.util.Set; + +import net.minecraft.item.ItemStack; + +import codechicken.nei.ItemStackAmount; +import codechicken.nei.bookmark.BookmarkItem; +import codechicken.nei.recipe.Recipe; +import codechicken.nei.recipe.Recipe.RecipeId; +import codechicken.nei.recipe.Recipe.RecipeIngredient; +import codechicken.nei.recipe.StackInfo; + +public class RecipeChainMath { + + public final Map outputRecipes = new HashMap<>(); + + public final List initialItems = new ArrayList<>(); + public final List recipeIngredients = new ArrayList<>(); + public final List recipeResults = new ArrayList<>(); + + public final Map preferredItems = new HashMap<>(); + public final Map requiredAmount = new HashMap<>(); + + private RecipeChainMath(List recipeItems, Set collapsedRecipes) { + final Map recipeState = new HashMap<>(); + final Map multipliers = new HashMap<>(); + + for (BookmarkItem item : recipeItems) { + if (item.recipeId != null) { + recipeState + .put(item.recipeId, recipeState.getOrDefault(item.recipeId, 0) | (item.isIngredient ? 1 : 2)); + } + } + + for (BookmarkItem item : recipeItems) { + if (recipeState.getOrDefault(item.recipeId, 0) != 3) { + this.initialItems.add(item.copy()); + } else if (item.isIngredient) { + this.recipeIngredients.add(item.copyWithAmount(0)); + } else { + this.recipeResults.add(item.copyWithAmount(0)); + multipliers.put( + item.recipeId, + Math.max(multipliers.getOrDefault(item.recipeId, 0L), item.getMultiplier())); + } + } + + for (Map.Entry entry : multipliers.entrySet()) { + if (entry.getValue() > 1 || collapsedRecipes.contains(entry.getKey())) { + collectPreferredItems(entry.getKey(), this.preferredItems, new HashSet<>()); + this.outputRecipes.put(entry.getKey(), entry.getValue()); + } + } + + while (true) { + Map maxReference = Collections.emptyMap(); + RecipeId maxRecipeId = null; + long maxMultiplier = 0; + int maxDepth = 0; + + for (Map.Entry entry : multipliers.entrySet()) { + final RecipeId recipeId = entry.getKey(); + if (!this.outputRecipes.containsKey(recipeId) && this.preferredItems.values().stream() + .noneMatch(resItem -> resItem.recipeId.equals(recipeId))) { + final Map references = new HashMap<>(this.preferredItems); + final int depth = collectPreferredItems(recipeId, references, new HashSet<>()); + + if (maxDepth < depth || maxDepth == depth && entry.getValue() > maxMultiplier) { + maxMultiplier = entry.getValue(); + maxReference = references; + maxRecipeId = recipeId; + maxDepth = depth; + } + } + } + + if (maxReference.isEmpty()) { + break; + } + + this.preferredItems.putAll(maxReference); + this.outputRecipes.put(maxRecipeId, multipliers.get(maxRecipeId)); + } + + for (Map.Entry entry : multipliers.entrySet()) { + if (!this.outputRecipes.containsKey(entry.getKey()) && this.preferredItems.values().stream() + .noneMatch(resItem -> resItem.recipeId.equals(entry.getKey()))) { + this.outputRecipes.put(entry.getKey(), entry.getValue()); + } + } + + for (Map.Entry entry : this.outputRecipes.entrySet()) { + if (entry.getValue() == 0 && this.preferredItems.values().stream() + .noneMatch(prefItem -> prefItem.recipeId.equals(entry.getKey()))) { + entry.setValue(1L); + } + } + } + + private int collectPreferredItems(RecipeId recipeId, Map preferredItems, + Set visited) { + int maxDepth = 0; + visited.add(recipeId); + + for (BookmarkItem ingrItem : this.recipeIngredients) { + if (ingrItem.factor > 0 && recipeId.equals(ingrItem.recipeId) && !preferredItems.containsKey(ingrItem)) { + BookmarkItem prefItem = null; + + for (BookmarkItem item : this.recipeResults) { + if (item.factor > (prefItem == null ? 0 : prefItem.factor) && item.containsItems(ingrItem) + && !visited.contains(item.recipeId)) { + prefItem = item; + } + } + + if (prefItem != null) { + preferredItems.put(ingrItem, prefItem); + maxDepth = Math + .max(maxDepth, collectPreferredItems(prefItem.recipeId, preferredItems, visited) + 1); + } + } + } + + visited.remove(recipeId); + return maxDepth; + } + + public static RecipeChainMath of(List chainItems, Set collapsedRecipes) { + return new RecipeChainMath(chainItems, collapsedRecipes); + } + + public static RecipeChainMath of(Recipe recipe, long multiplier) { + final List chainItems = new ArrayList<>(); + final RecipeId recipeId = recipe.getRecipeId(); + final ItemStack result = recipe.getResult(); + + chainItems.add(BookmarkItem.of(-1, result, StackInfo.getAmount(result), recipeId, false)); + + for (RecipeIngredient ingr : recipe.getIngredients()) { + chainItems.add( + BookmarkItem.of( + -1, + ingr.getItemStack(), + ingr.getAmount(), + recipeId, + true, + BookmarkItem.generatePermutations(ingr.getItemStack(), recipe))); + } + + for (BookmarkItem item : chainItems) { + item.amount *= multiplier; + } + + return new RecipeChainMath(chainItems, Collections.emptySet()); + } + + public ItemStackAmount getMissedItems() { + final ItemStackAmount missedItems = new ItemStackAmount(); + + for (BookmarkItem item : this.recipeResults) { + long amount = item.amount - this.requiredAmount.getOrDefault(item, 0L); + if (amount > 0) { + missedItems.add(item.getItemStack(amount)); + } + } + + for (BookmarkItem item : this.recipeIngredients) { + long amount = this.requiredAmount.containsKey(this.preferredItems.get(item)) ? 0 + : this.requiredAmount.getOrDefault(item, item.amount); + if (amount > 0) { + missedItems.add(item.getItemStack(amount)); + } + } + + for (BookmarkItem item : this.initialItems) { + if (this.requiredAmount.getOrDefault(item, -1L) == 0) { + missedItems.add(item.getItemStack()); + } + } + + return missedItems; + } + + private void resetCalculation() { + + for (BookmarkItem item : this.recipeIngredients) { + item.amount = 0; + } + + for (BookmarkItem item : this.recipeResults) { + item.amount = 0; + } + + this.preferredItems.clear(); + this.requiredAmount.clear(); + + for (RecipeId recipeId : this.outputRecipes.keySet()) { + collectPreferredItems(recipeId, this.preferredItems, new HashSet<>()); + } + + } + + public RecipeChainMath refresh() { + resetCalculation(); + + for (BookmarkItem prefItem : this.recipeResults) { + if (prefItem.factor > 0 && this.outputRecipes.containsKey(prefItem.recipeId)) { + final long prefAmount = prefItem.factor * this.outputRecipes.get(prefItem.recipeId); + + this.preferredItems.put(prefItem, prefItem); + calculateSuitableRecipe(prefItem, prepareAmount(prefItem, prefAmount), new ArrayList<>()); + this.preferredItems.remove(prefItem); + } + } + + for (BookmarkItem prefItem : this.recipeResults) { + if (prefItem.factor > 0 && this.outputRecipes.containsKey(prefItem.recipeId) + && this.requiredAmount.containsKey(prefItem)) { + final long prefAmount = prefItem.factor * this.outputRecipes.get(prefItem.recipeId); + this.requiredAmount.put(prefItem, this.requiredAmount.get(prefItem) - prefAmount); + } + } + + return this; + } + + private void prepareIngredients(RecipeId recipeId, long stepShift, List visited) { + for (BookmarkItem item : this.recipeIngredients) { + if (item.factor > 0 && recipeId.equals(item.recipeId)) { + calculateSuitableRecipe(item, prepareAmount(item, item.factor * stepShift), visited); + } + } + } + + private void calculateSuitableRecipe(BookmarkItem ingrItem, long ingrAmount, List visited) { + final BookmarkItem prefItem = this.preferredItems.get(ingrItem); + + if (prefItem == null) { + this.requiredAmount.put(ingrItem, this.requiredAmount.getOrDefault(ingrItem, 0L) + ingrAmount); + } else if (visited.contains(prefItem.recipeId)) { + this.requiredAmount.put(prefItem, this.requiredAmount.getOrDefault(prefItem, 0L) + ingrAmount); + } else { + this.requiredAmount.put(prefItem, this.requiredAmount.getOrDefault(prefItem, 0L) + ingrAmount); + final long shift = (long) Math + .ceil((this.requiredAmount.get(prefItem) - prefItem.amount) / (double) prefItem.factor); + + if (shift > 0) { + addShift(prefItem.recipeId, shift); + + visited.add(prefItem.recipeId); + prepareIngredients(prefItem.recipeId, shift, visited); + visited.remove(prefItem.recipeId); + } + } + + } + + private long prepareAmount(BookmarkItem ingrItem, long ingrAmount) { + if (ingrAmount == 0) return 0; + + for (BookmarkItem item : this.initialItems) { + if (item.containsItems(ingrItem)) { + long initAmount = Math.min(ingrAmount, item.amount - this.requiredAmount.getOrDefault(item, 0L)); + if (initAmount > 0) { + this.requiredAmount.put(item, this.requiredAmount.getOrDefault(item, 0L) + initAmount); + ingrAmount -= initAmount; + if (ingrAmount == 0) break; + } + } + } + + return ingrAmount; + } + + private void addShift(RecipeId recipeId, long shift) { + for (BookmarkItem item : this.recipeIngredients) { + if (recipeId.equals(item.recipeId)) { + item.amount += item.factor * shift; + } + } + + for (BookmarkItem item : this.recipeResults) { + if (recipeId.equals(item.recipeId)) { + item.amount += item.factor * shift; + } + } + } + +} diff --git a/src/main/java/codechicken/nei/recipe/chain/RecipeChainTooltipLineHandler.java b/src/main/java/codechicken/nei/recipe/chain/RecipeChainTooltipLineHandler.java new file mode 100644 index 00000000..cb27e261 --- /dev/null +++ b/src/main/java/codechicken/nei/recipe/chain/RecipeChainTooltipLineHandler.java @@ -0,0 +1,253 @@ +package codechicken.nei.recipe.chain; + +import java.awt.Dimension; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Comparator; +import java.util.List; + +import net.minecraft.item.ItemStack; +import net.minecraft.util.EnumChatFormatting; + +import codechicken.lib.gui.GuiDraw; +import codechicken.lib.gui.GuiDraw.ITooltipLineHandler; +import codechicken.nei.ItemSorter; +import codechicken.nei.ItemStackAmount; +import codechicken.nei.ItemsTooltipLineHandler; +import codechicken.nei.NEIClientConfig; +import codechicken.nei.NEIClientUtils; +import codechicken.nei.bookmark.BookmarkItem; +import codechicken.nei.recipe.StackInfo; + +public class RecipeChainTooltipLineHandler implements ITooltipLineHandler { + + public final int groupId; + public final boolean crafting; + protected final RecipeChainMath math; + protected final List initialItems; + + protected ItemsTooltipLineHandler available; + protected ItemsTooltipLineHandler inputs; + protected ItemsTooltipLineHandler outputs; + protected ItemsTooltipLineHandler remainder; + protected boolean lastShiftKey = false; + + protected Dimension size = new Dimension(); + + public RecipeChainTooltipLineHandler(int groupId, boolean crafting, RecipeChainMath math) { + this.groupId = groupId; + this.crafting = crafting; + this.math = math; + this.initialItems = new ArrayList<>(this.math.initialItems); + } + + private void onUpdate() { + final List available = new ArrayList<>(); + final List inputs = new ArrayList<>(); + final List outputs = new ArrayList<>(); + final List remainder = new ArrayList<>(); + final ItemStackAmount inventory = new ItemStackAmount(); + + if (lastShiftKey) { + inventory.addAll(Arrays.asList(NEIClientUtils.mc().thePlayer.inventory.mainInventory)); + } + + if (!this.math.outputRecipes.isEmpty()) { + this.math.initialItems.clear(); + + if (lastShiftKey) { + for (ItemStack stack : inventory.values()) { + final long invStackSize = inventory.get(stack); + + if (invStackSize > 0) { + this.math.initialItems.add(BookmarkItem.of(-1, stack.copy())); + } + } + } else { + this.math.initialItems.addAll(initialItems); + } + + this.math.refresh(); + + for (BookmarkItem item : math.initialItems) { + final long amount = math.requiredAmount.getOrDefault(item, 0L); + + if (amount > 0) { + if (lastShiftKey) { + available.add(item.getItemStack(amount)); + } else { + inputs.add(item.getItemStack(amount)); + } + } + + } + + for (BookmarkItem item : math.recipeIngredients) { + final long amount = math.requiredAmount.containsKey(math.preferredItems.get(item)) ? 0 + : math.requiredAmount.getOrDefault(item, item.amount); + + if (amount > 0) { + inputs.add(item.getItemStack(amount)); + } + } + + for (BookmarkItem item : math.recipeResults) { + final long amount = item.amount - math.requiredAmount.getOrDefault(item, 0L); + + if (amount > 0) { + if (math.outputRecipes.containsKey(item.recipeId)) { + outputs.add(item.getItemStack(amount)); + } else if (lastShiftKey) { + remainder.add(item.getItemStack(amount)); + } + } + } + + } else if (lastShiftKey) { + + for (BookmarkItem item : this.math.initialItems) { + if (inventory.contains(item.itemStack)) { + final long invAmount = inventory.get(item.itemStack) * item.fluidCellAmount; + + if ((item.amount - invAmount) > 0) { + inputs.add(item.getItemStack(item.amount - invAmount)); + } + + if (Math.min(item.amount, invAmount) > 0) { + available.add(item.getItemStack(Math.min(item.amount, invAmount))); + } + + } else { + inputs.add(item.getItemStack()); + } + } + + } + + inputs.sort( + Comparator.comparing((ItemStack stack) -> StackInfo.getFluid(stack) != null) + .thenComparing(ItemSorter.instance)); + outputs.sort( + Comparator.comparing((ItemStack stack) -> StackInfo.getFluid(stack) != null) + .thenComparing(ItemSorter.instance)); + remainder.sort( + Comparator.comparing((ItemStack stack) -> StackInfo.getFluid(stack) != null) + .thenComparing(ItemSorter.instance)); + + this.inputs = new ItemsTooltipLineHandler( + lastShiftKey ? NEIClientUtils.translate("bookmark.crafting_chain.missing") + : NEIClientUtils.translate("bookmark.crafting_chain.input"), + inputs, + true, + Integer.MAX_VALUE); + + this.available = new ItemsTooltipLineHandler( + NEIClientUtils.translate("bookmark.crafting_chain.available"), + available, + true, + Integer.MAX_VALUE); + + this.outputs = new ItemsTooltipLineHandler( + NEIClientUtils.translate("bookmark.crafting_chain.output"), + outputs, + true, + Integer.MAX_VALUE); + + this.remainder = new ItemsTooltipLineHandler( + NEIClientUtils.translate("bookmark.crafting_chain.remainder"), + remainder, + true, + Integer.MAX_VALUE); + + if (lastShiftKey) { + this.inputs.setLabelColor(EnumChatFormatting.RED); + this.available.setLabelColor(EnumChatFormatting.GREEN); + } + + if (!this.inputs.isEmpty() || !this.outputs.isEmpty() + || !this.remainder.isEmpty() + || !this.available.isEmpty()) { + + if (!this.math.outputRecipes.isEmpty()) { + this.size.height = 2 + GuiDraw.fontRenderer.FONT_HEIGHT; + } + + this.size.width = Math.max( + this.inputs.getSize().width, + Math.max( + this.outputs.getSize().width, + Math.max(this.remainder.getSize().width, this.available.getSize().width))); + + this.size.height += this.inputs.getSize().height + this.outputs.getSize().height + + this.remainder.getSize().height + + this.available.getSize().height; + } else { + this.size.height = this.size.width = 0; + } + + } + + @Override + public Dimension getSize() { + if (this.lastShiftKey != (this.lastShiftKey = NEIClientUtils.shiftKey()) || this.outputs == null) { + onUpdate(); + } + return this.size; + } + + @Override + public void draw(int x, int y) { + if (this.size.height == 0) return; + + if (!this.math.outputRecipes.isEmpty()) { + GuiDraw.fontRenderer.drawStringWithShadow( + EnumChatFormatting.AQUA + NEIClientUtils.translate("bookmark.crafting_chain"), + x, + y + 2, + 0xee555555); + + y += 2 + GuiDraw.fontRenderer.FONT_HEIGHT; + } + + if (NEIClientConfig.recipeChainDir() == 0) { + + if (!this.inputs.isEmpty()) { + this.inputs.draw(x, y); + y += this.inputs.getSize().height; + } + + if (!this.available.isEmpty()) { + this.available.draw(x, y); + y += this.available.getSize().height; + } + + if (!this.outputs.isEmpty()) { + this.outputs.draw(x, y); + y += this.outputs.getSize().height; + } + + } else { + + if (!this.outputs.isEmpty()) { + this.outputs.draw(x, y); + y += this.outputs.getSize().height; + } + + if (!this.inputs.isEmpty()) { + this.inputs.draw(x, y); + y += this.inputs.getSize().height; + } + + if (!this.available.isEmpty()) { + this.available.draw(x, y); + y += this.available.getSize().height; + } + } + + if (!this.remainder.isEmpty()) { + this.remainder.draw(x, y); + } + + } + +} diff --git a/src/main/resources/assets/nei/lang/en_US.lang b/src/main/resources/assets/nei/lang/en_US.lang index 9d74d1f7..33ebbb9a 100644 --- a/src/main/resources/assets/nei/lang/en_US.lang +++ b/src/main/resources/assets/nei/lang/en_US.lang @@ -21,29 +21,45 @@ nei.bookmark.pullBookmarkedItems.tip=Pull all bookmarked items from storage to i nei.bookmark.group=Bookmarks Group nei.bookmark.group.include_group=Create/Include Group -nei.bookmark.group.include_group.key=LMB + Drag nei.bookmark.group.exclude_group=Remove/Exclude Group -nei.bookmark.group.exclude_group.key=RMB + Drag nei.bookmark.group.toggle_mode=Toggle Group Mode -nei.bookmark.group.toggle_mode.key=Left Click +nei.bookmark.group.toggle_collapse=Toggle Collapse/Expand nei.bookmark.group.toggle_crafting_chain=Toggle Crafting Chain -nei.bookmark.group.toggle_crafting_chain.key=Right Click -nei.bookmark.group.sorting=Sorting -nei.bookmark.group.sorting.key=SHIFT + LMB + Drag +nei.bookmark.group.overlay_use=Fill Crafting Grid +nei.bookmark.group.sorting=Move Position nei.bookmark.group.remove_recipe=Remove Group nei.bookmark.group.pull_items=Pull all Items -nei.bookmark.group.pull_items_ingredients=Pull all Ingredients +nei.bookmark.group.pull_items_shift=Pull missing Items +nei.bookmark.group.craft=Craft Group +nei.bookmark.item.sorting=Move Position +nei.bookmark.remove_recipe=Remove Recipe +nei.bookmark.remove_item=Remove Item +nei.bookmark.add_item=Add Bookmark +nei.bookmark.add_item_with_recipe=Add Bookmark with Recipe +nei.bookmark.add_item_with_count=Add Bookmark with Count +nei.bookmark.change_quantity=Change Quantity +nei.bookmark.change_quantity_step=Change Quantity with Multiplier +nei.bookmark.recipe.toggle_collapse=Toggle Collapse/Expand nei.bookmark.crafting_chain=Crafting Chain nei.bookmark.crafting_chain.input=Ingredients +nei.bookmark.crafting_chain.missing=Missing Items nei.bookmark.crafting_chain.output=Results nei.bookmark.crafting_chain.remainder=Remainders - +nei.bookmark.crafting_chain.available=Available Items nei.itempanel.quantity.default=One stack nei.itempanel.loading=Loading Item List... nei.itempanel.deleteItem=Delete Item nei.itempanel.collapsed.button.tip=Collapse/Expand All Collapsible Items -nei.itempanel.collapsed.hint.expand=Alt-Click to expand (%d items) -nei.itempanel.collapsed.hint.collapse=Alt-Click to collapse (%d items) +nei.itempanel.collapsed.hint.expand=Expand Group (%d items) +nei.itempanel.collapsed.hint.collapse=Collapse Group (%d items) +nei.itempanel.overlay_recipe=Overlay Recipe +nei.itempanel.craft=Craft Recipe +nei.itempanel.fill_crafting_grid=Fill Crafting Grid +nei.itempanel.fill_crafting_grid_quantity=Fill Crafting Grid With Quantity +nei.itempanel.open_crafting=Recipe to make this item +nei.itempanel.open_usage=Recipes that use this item +nei.itempanel.copy_name=Copy Item Name +nei.itempanel.copy_oredict=Copy Item OreDictionary nei.enchant=Enchant nei.enchant.level=Level @@ -145,16 +161,13 @@ nei.options.keys.gui.remove_recipe=Remove Group from Bookmarks nei.options.keys.gui.bookmark_recipe=Add Bookmark with Recipe nei.options.keys.gui.bookmark_count=Add Bookmark with Count nei.options.keys.gui.bookmark_pull_items=Pull all Bookmarked Items -nei.options.keys.gui.bookmark_pull_items_ingredients=Pull all Bookmarked Ingredients -nei.options.keys.gui.bookmark_recipe_count=Add Bookmark with Recipe & Count nei.options.keys.gui.hide_bookmarks=Toggle Bookmark Panel Visibility -nei.options.keys.gui.overlay=Overlay Recipe -nei.options.keys.gui.overlay_hide=Hide Overlay Recipe -nei.options.keys.gui.overlay_use=Use Overlay Recipe +nei.options.keys.gui.overlay=Fill/Overlay Crafting Grid nei.options.keys.gui.getprevioussearch=Previous Search nei.options.keys.gui.getnextsearch=Next Search nei.options.keys.gui.next_tooltip=Next Tooltip nei.options.keys.gui.copy_name=Copy Item Name +nei.options.keys.gui.craft=Craft Recipe nei.options.keys.gui.copy_oredict=Copy Item OreDictionary nei.options.keys.gui.itemzoom_toggle=Toggle Item Zoom nei.options.keys.gui.itemzoom_hold=Hold to Display Item Zoom @@ -181,23 +194,39 @@ nei.options.inventory.bookmarks.enabled.false=Hidden nei.options.inventory.bookmarks.animation=REI Style Animation in Bookmarks nei.options.inventory.bookmarks.animation.true=Yes nei.options.inventory.bookmarks.animation.false=No -nei.options.inventory.bookmarks.craftingChainDir=Crafting Chain Info Direction -nei.options.inventory.bookmarks.craftingChainDir.0=Input First -nei.options.inventory.bookmarks.craftingChainDir.1=Output First -nei.options.inventory.bookmarks.recipeTooltipsMode=Show recipe tooltips in Bookmarks +nei.options.inventory.bookmarks.recipeChainDir=Crafting Chain Info Direction +nei.options.inventory.bookmarks.recipeChainDir.0=Input First +nei.options.inventory.bookmarks.recipeChainDir.1=Output First +nei.options.inventory.bookmarks.recipeTooltipsMode=Show Recipe Tooltips in Bookmarks nei.options.inventory.bookmarks.recipeTooltipsMode.0=None nei.options.inventory.bookmarks.recipeTooltipsMode.1=In Horizontal View nei.options.inventory.bookmarks.recipeTooltipsMode.2=In Vertical View nei.options.inventory.bookmarks.recipeTooltipsMode.3=Always -nei.options.inventory.bookmarks.showRecipeMarker=Show Recipe Marker -nei.options.inventory.bookmarks.showRecipeMarker.true=Yes -nei.options.inventory.bookmarks.showRecipeMarker.false=No +nei.options.inventory.bookmarks.showRecipeMarkerMode=Show Recipe Marker +nei.options.inventory.bookmarks.showRecipeMarkerMode.0=None +nei.options.inventory.bookmarks.showRecipeMarkerMode.1=Icon +nei.options.inventory.bookmarks.showRecipeMarkerMode.2=Background +nei.options.inventory.bookmarks.recipeMarkerColor=Recipe Marker Color nei.options.inventory.bookmarks.ignorePotionOverlap=Ignore overlap with potion effect HUD nei.options.inventory.bookmarks.ignorePotionOverlap.true=Yes nei.options.inventory.bookmarks.ignorePotionOverlap.false=No -nei.options.inventory.bookmarks.worldSpecific=World Specific Bookmarks +nei.options.inventory.bookmarks.worldSpecific=World Specific nei.options.inventory.bookmarks.worldSpecific.true=Yes nei.options.inventory.bookmarks.worldSpecific.false=No +nei.options.inventory.favorites=Favorites +nei.options.inventory.favorites.enabled=Visibility +nei.options.inventory.favorites.enabled.true=Visible +nei.options.inventory.favorites.enabled.false=Hidden +nei.options.inventory.favorites.worldSpecific=World Specific +nei.options.inventory.favorites.worldSpecific.true=Yes +nei.options.inventory.favorites.worldSpecific.false=No +nei.options.inventory.favorites.showRecipeTooltipInPanel=Show Recipe Tooltips in Right Panel +nei.options.inventory.favorites.showRecipeTooltipInPanel.true=Yes +nei.options.inventory.favorites.showRecipeTooltipInPanel.false=No +nei.options.inventory.favorites.showRecipeTooltipInGui=Show Recipe Tooltips in Recipe Gui +nei.options.inventory.favorites.showRecipeTooltipInGui.true=Yes +nei.options.inventory.favorites.showRecipeTooltipInGui.false=No +nei.options.inventory.favorites.depth=Depth nei.options.inventory.hotkeysHelpText=Show Hotkeys Help Text nei.options.inventory.hotkeysHelpText.true=Yes nei.options.inventory.hotkeysHelpText.false=No @@ -310,6 +339,7 @@ nei.options.inventory.gamemodes.tip=Select the gamemodes to cycle through nei.options.inventory.gamemodes.creative=Creative nei.options.inventory.gamemodes.creative+=Creative+ nei.options.inventory.gamemodes.adventure=Adventure +nei.options.inventory.itempanelScale=Item Panel Scale (in percent) nei.options.inventory.disableMouseScrollTransfer=Disable Mouse Wheel Transfer nei.options.inventory.disableMouseScrollTransfer.true=True nei.options.inventory.disableMouseScrollTransfer.false=False @@ -452,16 +482,18 @@ nei.recipe.profiler.usage=Usage Profiling 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.overlay=Recipe Overlay nei.recipe.overlay.missing=Missing following nei.recipe.overlay.mismatch=Mismatch Crafting Grid +nei.recipe.overlay.overlayRecipe=Overlay Recipe nei.recipe.overlay.fillCraftingGrid=Fill Crafting Grid -nei.recipe.overlay.fillCraftingGrid.key=SHIFT + Click -nei.recipe.overlay.bookmark=Save to Bookmark nei.recipe.overlay.bookmarkRecipe=Save with Recipe -nei.recipe.overlay.bookmarkAmount=Save with Amount -nei.recipe.overlay.bookmarkRecipeAndAmount=Save with Recipe & Amount + +nei.recipe.favorite=Favorite +nei.recipe.favorite.bookmark_recipe=Save Recipe Tree +nei.recipe.favorite.change_result_index=Change Output nei.itemsort.minecraft=Minecraft nei.itemsort.minecraft.tip=Sorts items from vanilla minecraft first diff --git a/src/main/resources/assets/nei/lang/ru_RU.lang b/src/main/resources/assets/nei/lang/ru_RU.lang index c9f07f17..f4fc6366 100644 --- a/src/main/resources/assets/nei/lang/ru_RU.lang +++ b/src/main/resources/assets/nei/lang/ru_RU.lang @@ -13,18 +13,13 @@ nei.bookmark.pullBookmarkedItems.tip=Переместить все отмече nei.bookmark.group=Группа закладок nei.bookmark.group.include_group=Создать/Включить группу -nei.bookmark.group.include_group.key=ЛКМ + Перетаскивание nei.bookmark.group.exclude_group=Удалить/Исключить группу -nei.bookmark.group.exclude_group.key=ПКМ + Перетаскивание nei.bookmark.group.toggle_mode=Переключить режим группы -nei.bookmark.group.toggle_mode.key=Левый клик nei.bookmark.group.toggle_crafting_chain=Переключить цепочку крафта -nei.bookmark.group.toggle_crafting_chain.key=Правый клик nei.bookmark.group.sorting=Сортировка -nei.bookmark.group.sorting.key=SHIFT + ЛКМ + Перетаскивание nei.bookmark.group.remove_recipe=Удалить группу nei.bookmark.group.pull_items=Переместить все предметы -nei.bookmark.group.pull_items_ingredients=Переместить все ингредиенты +nei.bookmark.group.pull_items_shift=Переместить недостающие предметы nei.bookmark.crafting_chain=Цепочка крафта nei.bookmark.crafting_chain.input=Ингредиенты nei.bookmark.crafting_chain.output=Результаты @@ -34,8 +29,8 @@ nei.itempanel.quantity.default=Одна стопка nei.itempanel.loading=Загрузка списка предметов... nei.itempanel.deleteItem=Удалить предмет nei.itempanel.collapsed.button.tip=Свернуть/Развернуть все группы предметов -nei.itempanel.collapsed.hint.expand=Alt-Клик для разворачивания (%d предметов) -nei.itempanel.collapsed.hint.collapse=Alt-Клик для сворачивания (%d предметов) +nei.itempanel.collapsed.hint.expand=Развернуть группу (%d предметов) +nei.itempanel.collapsed.hint.collapse=Свернуть группу (%d предметов) nei.enchant=Зачаровать nei.enchant.level=Уровень @@ -137,12 +132,8 @@ nei.options.keys.gui.remove_recipe=Удалить группу из заклад nei.options.keys.gui.bookmark_recipe=Добавить закладку с рецептом nei.options.keys.gui.bookmark_count=Добавить закладку с учётом количества nei.options.keys.gui.bookmark_pull_items=Переместить все отмеченные предметы -nei.options.keys.gui.bookmark_pull_items_ingredients=Переместить все отмеченные ингредиенты -nei.options.keys.gui.bookmark_recipe_count=Добавить закладку с рецептом, учитывающую количество nei.options.keys.gui.hide_bookmarks=Переключить видимость панели закладок -nei.options.keys.gui.overlay=Отображение рецепта -nei.options.keys.gui.overlay_hide=Скрыть отображение рецепта -nei.options.keys.gui.overlay_use=Использовать отображение рецепта +nei.options.keys.gui.overlay=Заполнить сетку крафта nei.options.keys.gui.getprevioussearch=Предыдущий поиск nei.options.keys.gui.getnextsearch=Следующий поиск nei.options.keys.gui.next_tooltip=Следующая подсказка @@ -173,17 +164,18 @@ nei.options.inventory.bookmarks.enabled.false=Скрытая nei.options.inventory.bookmarks.animation=Анимация в стиле REI в закладках nei.options.inventory.bookmarks.animation.true=Да nei.options.inventory.bookmarks.animation.false=Нет -nei.options.inventory.bookmarks.craftingChainDir=Направление информации о цепочке крафта -nei.options.inventory.bookmarks.craftingChainDir.0=Сначала ингредиенты -nei.options.inventory.bookmarks.craftingChainDir.1=Сначала результат +nei.options.inventory.bookmarks.recipeChainDir=Направление информации о цепочке крафта +nei.options.inventory.bookmarks.recipeChainDir.0=Сначала ингредиенты +nei.options.inventory.bookmarks.recipeChainDir.1=Сначала результат nei.options.inventory.bookmarks.recipeTooltipsMode=Показывать подсказки рецептов в закладках nei.options.inventory.bookmarks.recipeTooltipsMode.0=Нет nei.options.inventory.bookmarks.recipeTooltipsMode.1=В горизонтальном виде nei.options.inventory.bookmarks.recipeTooltipsMode.2=В вертикальном виде nei.options.inventory.bookmarks.recipeTooltipsMode.3=Всегда -nei.options.inventory.bookmarks.showRecipeMarker=Показывать маркер рецепта -nei.options.inventory.bookmarks.showRecipeMarker.true=Да -nei.options.inventory.bookmarks.showRecipeMarker.false=Нет +nei.options.inventory.bookmarks.showRecipeMarkerMode=Показывать маркер рецепта +nei.options.inventory.bookmarks.showRecipeMarkerMode.0=Нет +nei.options.inventory.bookmarks.showRecipeMarkerMode.1=Иконка +nei.options.inventory.bookmarks.showRecipeMarkerMode.2=Фон nei.options.inventory.bookmarks.ignorePotionOverlap=Игнорировать перекрытие с HUD эффектов зелий nei.options.inventory.bookmarks.ignorePotionOverlap.true=Да nei.options.inventory.bookmarks.ignorePotionOverlap.false=Нет @@ -416,11 +408,7 @@ nei.recipe.overlay=отображение рецепта nei.recipe.overlay.missing=Отсутствует следующее nei.recipe.overlay.mismatch=Несоответствие сетки крафта nei.recipe.overlay.fillCraftingGrid=Заполнить сетку крафта -nei.recipe.overlay.fillCraftingGrid.key=SHIFT + Клик -nei.recipe.overlay.bookmark=Сохранить в закладки nei.recipe.overlay.bookmarkRecipe=Сохранить с рецептом -nei.recipe.overlay.bookmarkAmount=Сохранить с учётом количества -nei.recipe.overlay.bookmarkRecipeAndAmount=Сохранить с рецептом и количеством nei.itemsort.minecraft=Minecraft nei.itemsort.minecraft.tip=Сортирует предметы из ванильного Minecraft первыми diff --git a/src/main/resources/assets/nei/lang/zh_CN.lang b/src/main/resources/assets/nei/lang/zh_CN.lang index e814ef0e..550ad77c 100644 --- a/src/main/resources/assets/nei/lang/zh_CN.lang +++ b/src/main/resources/assets/nei/lang/zh_CN.lang @@ -105,11 +105,8 @@ nei.options.keys.gui.search=跳转到搜索框 nei.options.keys.gui.bookmark=添加书签 nei.options.keys.gui.bookmark_recipe=添加带有合成表的书签 nei.options.keys.gui.bookmark_count=添加带有数量的书签 -nei.options.keys.gui.bookmark_recipe_count=添加带有合成表和数量的书签 nei.options.keys.gui.hide_bookmarks=显示/隐藏书签页 -nei.options.keys.gui.overlay=提取合成表至合成栏/工作台 -nei.options.keys.gui.overlay_hide=隐藏覆盖合成表 -nei.options.keys.gui.overlay_use=使用覆盖合成表 +nei.options.keys.gui.overlay=使用覆盖合成表 nei.options.keys.gui.getprevioussearch=上一个搜索 nei.options.keys.gui.getnextsearch=下一个搜索 nei.options.keys.gui.copy_name=复制物品名称 @@ -144,8 +141,7 @@ nei.options.inventory.bookmarks.animation=REI风格书签动画 nei.options.inventory.bookmarks.animation.true=是 nei.options.inventory.bookmarks.animation.false=否 nei.options.inventory.bookmarks.showRecipeMarker=在书签栏显示合成产物标记 -nei.options.inventory.bookmarks.showRecipeMarker.true=是 -nei.options.inventory.bookmarks.showRecipeMarker.false=否 +nei.options.inventory.bookmarks.showRecipeMarker.0=否 nei.options.inventory.showItemQuantityWidget=显示物品数量栏(右下角) nei.options.inventory.showItemQuantityWidget.true=是 nei.options.inventory.showItemQuantityWidget.false=否 diff --git a/src/main/resources/assets/nei/textures/nei_sprites.png b/src/main/resources/assets/nei/textures/nei_sprites.png index e7afa6cd..224637e5 100644 Binary files a/src/main/resources/assets/nei/textures/nei_sprites.png and b/src/main/resources/assets/nei/textures/nei_sprites.png differ