Compare commits

...
4 Commits
Author SHA1 Message Date
Martin Robertz 68fcc34d18 update 2025-03-29 11:09:29 +01:00
slprime 3ff3bb5c11 recipe tooltip refactoring 2025-03-28 23:19:57 +02:00
slprime 903271b279 remove ingredients sorting 2025-03-27 10:59:46 +02:00
slprime 204c37fa36 Create Favorites Widget; More hotkeys; More Tooltips; 2025-03-26 08:15:00 +02:00
61 changed files with 8343 additions and 3938 deletions

No files matched your search

+1 -1
View File
@@ -2,7 +2,7 @@
dependencies {
api('com.github.GTNewHorizons:CodeChickenCore:1.4.1:dev')
implementation('com.github.GTNewHorizons:GTNHLib:0.6.16:dev')
implementation('com.github.GTNewHorizons:GTNHLib:0.6.17:dev')
shadowImplementation('org.apache.commons:commons-csv:1.10.0')
@@ -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<ItemStack, ItemStack> calculatedItems = new HashMap<>();
public HashMap<ItemStack, Integer> multiplier = new HashMap<>();
public HashMap<ItemStack, ItemStack> inputs = new HashMap<>();
public HashMap<ItemStack, ItemStack> outputs = new HashMap<>();
public HashMap<ItemStack, ItemStack> remainder = new HashMap<>();
public HashMap<ItemStack, ItemStack> 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<ItemStack, Integer> 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<FluidStack, Integer> 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<BookmarkRecipeId, Integer> 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<CraftingChainItem> items = new ArrayList<>();
public HashMap<Integer, Integer> multiplier = new HashMap<>();
public HashMap<String, Long> counts = new HashMap<>();
public HashMap<Integer, HashSet<CraftingChainItem>> inputs = new HashMap<>();
public HashMap<Integer, HashSet<CraftingChainItem>> outputs = new HashMap<>();
public HashSet<Integer> initialItems = new HashSet<>();
public HashSet<Integer> startedIngrs = new HashSet<>();
public CraftingChainRequest(List<ItemStack> items, List<ItemStackMetadata> metadata) {
HashSet<Integer> inputs = new HashSet<>();
HashSet<Integer> 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<ItemStack> items, List<ItemStackMetadata> metadata) {
CraftingChainRequest request = new CraftingChainRequest(items, metadata);
HashMap<Integer, Long> iShift = new HashMap<>();
HashMap<Integer, Long> 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<CraftingChainItem> items) {
return calculateCount(request, items, -1);
}
private long calculateCount(CraftingChainRequest request, HashSet<CraftingChainItem> 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;
}
}
File diff suppressed because it is too large. Load diff
@@ -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<NBTTagCompound, RecipeId> items = new HashMap<>();
private static final Map<String, RecipeId> 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<String> 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<Map.Entry<NBTTagCompound, RecipeId>> 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<String> strings = new ArrayList<>();
for (Map.Entry<NBTTagCompound, RecipeId> 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);
}
}
}
@@ -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<String> handleItemDisplayName(GuiContainer gui, ItemStack itemstack, List<String> currenttip) {
return currenttip;
}
@Override
public List<String> handleItemTooltip(GuiContainer gui, ItemStack itemstack, int mousex, int mousey,
List<String> currenttip) {
return currenttip;
}
@Override
public List<String> handleTooltip(GuiContainer gui, int mousex, int mousey, List<String> currenttip) {
@@ -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<ItemsGridSlot, MouseContext> grid = new ItemsGrid<>() {
protected List<ItemsGridSlot> gridMask;
@Override
protected void onGridChanged() {
this.gridMask = null;
super.onGridChanged();
}
@Override
public List<ItemsGridSlot> 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;
}
}
+12 -1
View File
@@ -7,6 +7,8 @@ import java.util.HashMap;
import java.util.HashSet;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ForkJoinPool;
import java.util.concurrent.ForkJoinWorkerThread;
@@ -27,6 +29,7 @@ import codechicken.nei.ThreadOperationTimer.TimeoutException;
import codechicken.nei.api.ItemFilter;
import codechicken.nei.api.ItemFilter.ItemFilterProvider;
import codechicken.nei.api.ItemInfo;
import codechicken.nei.recipe.StackInfo;
import codechicken.nei.search.TooltipFilter;
public class ItemList {
@@ -365,6 +368,7 @@ public class ItemList {
loadFinished = true;
SubsetWidget.updateHiddenItems();
ItemPanels.bookmarkPanel.load();
updateFilter.restart();
}
};
@@ -411,7 +415,7 @@ public class ItemList {
if (interrupted()) return;
Comparator<ItemStack> comparator = Comparator.comparingInt(ItemList.ordering::get);
filtered.sort(comparator::compare);
filtered.sort(comparator);
if (interrupted()) return;
@@ -419,6 +423,13 @@ public class ItemList {
}
};
public static int getItemOrderIndex(ItemStack stack) {
final Optional<Map.Entry<ItemStack, Integer>> orderingEntry = ItemList.ordering.entrySet().parallelStream()
.filter(entry -> StackInfo.equalItemAndNBT(entry.getKey(), stack, true)).findAny();
return orderingEntry.isPresent() ? orderingEntry.get().getValue() : 0;
}
/**
* @deprecated Use updateFilter.restart()
*/
+64 -327
View File
@@ -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<ItemsPanelGrid> {
/**
* 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<ItemStack> newItems;
public ArrayList<ItemStack> rawItems;
protected final HashMap<Integer, List<ItemStack>> groupItems = new HashMap<>();
protected final HashMap<Integer, MaskMetadata> maskMetadata = new HashMap<>();
protected boolean forceExpand = false;
public void setItems(ArrayList<ItemStack> 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<Integer> 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<Integer> getMask() {
final boolean updateBorders = this.gridMask == null;
final List<Integer> mask = super.getMask();
if (updateBorders) {
calculateGroupBorders(mask);
}
return mask;
}
protected void calculateGroupBorders(List<Integer> mask) {
this.maskMetadata.clear();
if (mask.isEmpty() || this.groupItems.isEmpty()) {
return;
}
final HashMap<Integer, Integer> 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<Integer> 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<ItemStack> 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<ItemStack> 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<String> handleItemTooltip(GuiContainer gui, ItemStack itemstack, int mousex, int mousey,
List<String> 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<ItemStack> 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<String, String> handleHotkeys(GuiContainer gui, int mousex, int mousey, Map<String, String> 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;
}
}
@@ -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")) {
@@ -22,6 +22,12 @@ public class ItemStackAmount {
}
}
public void addAll(Iterable<ItemStack> 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();
}
+191 -103
View File
@@ -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<T extends ItemsGrid.ItemsGridSlot, M extends ItemsGrid.MouseContext> {
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 <M extends MouseContext> 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 <M extends MouseContext> 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 / DEFAULT_SLOT_SIZE;
GL11.glTranslatef(rect.x + panelFactor, rect.y + panelFactor, 0);
GL11.glScaled(panelFactor, panelFactor, 1);
GuiContainerManager.drawItem(0, 0, stack, true, "");
GL11.glScaled(1f / panelFactor, 1f / panelFactor, 1);
GL11.glTranslatef(-1 * (rect.x + panelFactor), -1 * (rect.y + panelFactor), 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<Integer> 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<T> 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<Integer> 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<Integer> getMask() {
public abstract List<T> 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<Integer> 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<Integer> 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<Integer> 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<Integer> 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() {
@@ -0,0 +1,242 @@
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<ItemsPanelGrid.ItemsPanelGridSlot, ItemsPanelGrid.MouseContext> {
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) {
int scale = (int) Math.ceil(Math.min(rect.w, rect.h) / DEFAULT_SLOT_SIZE);
GuiContainerManager.drawItems.zLevel -= 10F;
super.drawItem(getBGItem(), rect.offset(1 * scale, -1 * scale));
GuiContainerManager.drawItems.zLevel += 10F;
super.drawItem(getItemStack(), rect.offset(-2 * scale, 2 * scale));
} else {
super.drawItem(rect);
}
}
}
public ArrayList<ItemStack> newItems;
public ArrayList<ItemStack> rawItems;
protected List<ItemsPanelGridSlot> gridMask;
protected final Map<Integer, Integer> itemToGroup = new HashMap<>();
protected final Map<Integer, List<ItemStack>> groupItems = new HashMap<>();
protected boolean forceExpand = false;
public void setItems(ArrayList<ItemStack> items) {
newItems = items;
}
public static void updateScale() {
SLOT_SIZE = (int) Math.ceil(9f * 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<Integer> 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<ItemsPanelGridSlot> 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<ItemsPanelGridSlot> 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<Integer, Integer> 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<ItemStack> 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");
}
}
@@ -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<ItemStack> 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<ItemStack> groupingItemStacks(List<ItemStack> items) {
final List<ItemStack> result = ItemStackAmount.of(items).values();
final List<ItemStack> result = new ArrayList<>();
for (ItemStack stack : result) {
if (StackInfo.itemStackToNBT(stack).hasKey("gtFluidName")) {
for (Map.Entry<NBTTagCompound, Long> 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;
@@ -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);
@@ -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,11 @@ 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.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);
@@ -313,6 +315,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 +342,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 +364,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 +683,32 @@ 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.showRecipeTooltip").setComment("Show recipe tooltips in Items Panel")
.getBooleanValue(true);
API.addOption(new OptionToggleButton("inventory.favorites.showRecipeTooltip", 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 +723,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 +810,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 +869,7 @@ public class NEIClientConfig {
}
public static void bootNEI(World world) {
if (!mainNEIConfigLoaded) {
// main NEI config loading
ItemInfo.load(world);
@@ -868,10 +901,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 +918,10 @@ public class NEIClientConfig {
}
}.start();
} else {
ItemPanels.bookmarkPanel.load();
SubsetWidget.loadHidden();
ItemList.loadItems.restart();
}
}
public static boolean isWorldSpecific(String setting) {
@@ -909,6 +942,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 +962,12 @@ public class NEIClientConfig {
return getIntSetting("inventory.bookmarks.recipeTooltipsMode");
}
public static boolean showRecipeMarker() {
return getBooleanSetting("inventory.bookmarks.showRecipeMarker");
public static boolean getFavoriteRecipeTooltips() {
return getBooleanSetting("inventory.favorites.showRecipeTooltip");
}
public static int showRecipeMarkerMode() {
return getIntSetting("inventory.bookmarks.showRecipeMarkerMode");
}
public static boolean showItemQuantityWidget() {
@@ -973,8 +1018,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() {
@@ -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,9 +44,11 @@ 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;
@@ -486,4 +491,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 drawMarker(float x, float y, float scale, String text, int color, boolean alignRight,
boolean alignBottom) {
final float screenScale = mc().currentScreen.width * 1f / mc().displayWidth;
final float smallTextScale = Math
.max(screenScale, Math.max(scale, 1f) * (GuiDraw.fontRenderer.getUnicodeFlag() ? 3F / 4F : 1F / 2F));
NEIClientUtils.gl2DRenderContext(() -> {
double offsetX = Math.ceil(x);
double offsetY = Math.ceil(y);
if (alignRight) {
offsetX = Math.ceil(x + 16 * scale - GuiDraw.fontRenderer.getStringWidth(text) * smallTextScale);
}
if (alignBottom) {
offsetY = Math
.ceil(y + 16 * scale - GuiDraw.fontRenderer.FONT_HEIGHT * smallTextScale + smallTextScale);
}
GL11.glTranslated(offsetX, offsetY, 0);
GL11.glScalef(smallTextScale, smallTextScale, 1f);
GuiDraw.fontRenderer.drawStringWithShadow(text, 0, 0, color);
GL11.glScalef(1f / smallTextScale, 1f / smallTextScale, 1f);
GL11.glTranslated(-1 * offsetX, -1 * offsetY, 0);
});
}
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);
}
}
+56 -72
View File
@@ -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<T extends ItemsGrid<? extends ItemsGridSlot, ? extends ItemsGrid.MouseContext>>
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<ItemStack> 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<String> handleTooltip(GuiContainer gui, int mousex, int mousey, List<String> currenttip) {
return currenttip;
}
@Override
public List<String> handleItemDisplayName(GuiContainer gui, ItemStack itemstack, List<String> currenttip) {
return currenttip;
}
@Override
public List<String> handleItemTooltip(GuiContainer gui, ItemStack itemstack, int mousex, int mousey,
List<String> currenttip) {
return currenttip;
return this.grid.contains(px, py);
}
}
@@ -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<ItemStack> getFilteredPermutations(ItemFilter additionalFilter) {
List<ItemStack> items = Arrays.asList(this.items).stream().filter(getItemFilter(additionalFilter)::matches)
.collect(Collectors.toList());
List<ItemStack> 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<ItemStack> filteringPermutations(List<ItemStack> items, ItemFilter filter) {
if (filter == null) return items;
final List<ItemStack> filteredItems = items.stream().filter(filter::matches).collect(Collectors.toList());
return filteredItems.isEmpty() ? items : filteredItems;
}
public boolean setPermutationToRender(ItemStack ingredient) {
@@ -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) {
@@ -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;
@@ -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<ItemStack> getStorageStacks(GuiContainer guiContainer) {
final List<Slot> slots = guiContainer.inventorySlots.inventorySlots;
final List<ItemStack> 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<ItemStack> realItems);
}
@@ -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<ItemOverlayState> presenceOverlay(GuiContainer firstGui, IRecipeHandler recipe, int recipeIndex) {
final List<ItemOverlayState> 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<ItemStack> used = invStacks.stream().filter(is -> is.stackSize > 0 && stack.contains(is))
@@ -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,311 @@ 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<PositionedStack> 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<ItemStack> 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<String, String> handleHotkeys(GuiContainer gui, int mousex, int mousey, ItemStack stack) {
return new HashMap<>();
final int groupId = ItemPanels.bookmarkPanel.getHoveredGroupId(true);
final Map<String, String> 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() ? "itempanel.remove_item" : "itempanel.remove_recipe"));
if (slot.isIngredient()) {
hotkeys.put(
NEIClientConfig.getKeyName("gui.bookmark", NEIClientUtils.SHIFT_HASH),
NEIClientUtils.translate("itempanel.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("itempanel.add_item"));
if (StackInfo.getAmount(stack) > 0) {
hotkeys.put(
NEIClientConfig.getKeyName("gui.bookmark", NEIClientUtils.CTRL_HASH),
NEIClientUtils.translate("itempanel.add_item_with_count"));
}
if (recipeId != null) {
hotkeys.put(
NEIClientConfig.getKeyName("gui.bookmark", NEIClientUtils.SHIFT_HASH),
NEIClientUtils.translate("itempanel.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;
}
}
File diff suppressed because it is too large. Load diff
@@ -0,0 +1,382 @@
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.ItemList;
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<BookmarksGridSlot> gridMask = null;
public final Map<Integer, Integer> rowToGroupId = new HashMap<>();
public final Map<Integer, RecipeId> itemToRecipe = new HashMap<>();
public final Map<Integer, Integer> slotToItem = new LinkedHashMap<>();
public final Map<Integer, Integer> itemToSlot = new LinkedHashMap<>();
public final Map<Integer, BookmarkChainItem> 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<Integer, String> 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<Integer> 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<Integer> results = getGroupResults(bookmarkItem.groupId, itemIndex, size);
List<Integer> 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<Integer> getGroupResults(int groupId, int itemIndex, int size) {
final BookmarkGroup group = this.grid.getGroup(groupId);
final List<Integer> 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<Integer, String> borderGrid) {
final BookmarkItem bookmarkItem = this.grid.getBookmarkItem(itemIndex);
final BookmarkGroup group = this.grid.getGroup(bookmarkItem.groupId);
final List<Integer> 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<Integer> 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 || !meta.isIngredient
|| previousMeta == null
|| previousMeta.groupId != meta.groupId
|| index + 1 < maxIndex && this.grid.isInvalidSlot((index + 1) % maxIndex)
|| meta.isIngredient && !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
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<Integer> generateSortingComparator(Map<Integer, BookmarkChainItem> calculatedItems) {
return Comparator.comparingInt((Integer index) -> calculatedItems.get(index).calculatedType.toInt())
.thenComparing(index -> StackInfo.getFluid(calculatedItems.get(index).getItem().itemStack) != null)
.thenComparing(index -> ItemList.getItemOrderIndex(calculatedItems.get(index).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<Integer, String> 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));
}
}
}
@@ -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<RecipeId> 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);
}
}
@@ -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<String, ItemStack> permutations;
public long factor;
public RecipeId recipeId;
public boolean isIngredient = false;
protected BookmarkItem(int groupId, long amount, int fluidCellAmount, ItemStack itemStack,
Map<String, ItemStack> 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<String, ItemStack> 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<String, ItemStack> 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<String, ItemStack> 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<String, ItemStack> 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;
}
}
@@ -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<BookmarkGrid> 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<String> 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<BookmarkGrid> 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<String, JsonElement> 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<BookmarkGrid> namespaces) {
for (BookmarkGrid grid : namespaces) {
if (grid.bookmarkItems.stream()
.noneMatch(item -> item.recipeId != null && item.recipeId.getResult() == null)) {
continue;
}
final Map<String, RecipeId> 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<String> 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);
}
}
}
@@ -0,0 +1,312 @@
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.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
&& (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 <M extends MouseContext> 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 <M extends MouseContext> 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),
0xAAAAAA);
} else if (shownItemType == ShownItemType.SHIFT) {
drawMarker(rect, "x" + ReadableNumberConverter.INSTANCE.toWideReadableForm(getMultiplier()), 0xFFFFFF);
} else if (this.isOutputRecipe) {
drawMarker(
rect,
"x" + ReadableNumberConverter.INSTANCE.toWideReadableForm(this.realMultiplier),
0xAAAAAA);
} else if (NEIClientConfig.showRecipeMarkerMode() == 1) {
drawMarker(rect, "R", 0xA0A0A0);
}
} else if (NEIClientConfig.showRecipeMarkerMode() == 1) {
drawMarker(rect, "R", 0xA0A0A0);
}
}
protected void drawMarker(Rectangle4i rect, String text, int color) {
final float panelFactor = rect.w / DEFAULT_SLOT_SIZE;
NEIClientUtils.drawMarker(rect.x + panelFactor, rect.y + panelFactor, panelFactor, text, color, false, false);
}
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 / DEFAULT_SLOT_SIZE;
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.drawMarker(
rect.x + panelFactor,
rect.y + panelFactor,
panelFactor,
amountString,
0xFFFFFF,
!this.isFluidDisplay,
true);
}
}
}
@@ -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<Integer, Integer> prepareGroups(Map<Integer, Integer> 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<Integer> 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<RecipeId> 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<Integer> 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<RecipeId> 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;
}
}
@@ -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<Integer, BookmarkChainItem> calculatedItems = new HashMap<>();
public final Map<Integer, RecipeId> itemToRecipe = new HashMap<>();
public final Set<RecipeId> outputRecipes = new HashSet<>();
public final Set<RecipeId> recipeInMiddle = new HashSet<>();
public final Map<RecipeId, Set<RecipeId>> recipeRelations = new HashMap<>();
public void refresh(Map<Integer, BookmarkItem> chainItems, Set<RecipeId> collapsedRecipes) {
final Map<BookmarkItem, Integer> sortingChainItems = new HashMap<>();
for (Map.Entry<Integer, BookmarkItem> 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<RecipeId> 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<Integer, BookmarkItem> 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<RecipeId> getRecipeRelations(RecipeChainMath math, RecipeId recipeId, Set<RecipeId> topLevelRecipes,
Set<RecipeId> 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<RecipeId> topLevelRecipes,
Map<BookmarkItem, Integer> sortingChainItems) {
final List<BookmarkChainItem> 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<BookmarkItem> chainItems,
Map<BookmarkItem, Integer> sortingChainItems) {
final Set<RecipeId> recipeRelations = this.recipeRelations.get(recipeId);
final List<BookmarkItem> 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<BookmarkChainItem> 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<BookmarkChainItem> collectItems(RecipeChainMath math, RecipeId recipeId, Set<RecipeId> recipeRelations,
Set<RecipeId> topLevelRecipes) {
final Map<String, BookmarkChainItem> items = new HashMap<>();
final Map<RecipeId, Long> 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<RecipeId> findTopLevelRecipes(RecipeChainMath math, Set<RecipeId> collapsedRecipes) {
final Set<RecipeId> result = new HashSet<>(math.outputRecipes.keySet());
for (BookmarkItem item : math.recipeResults) {
if (!result.contains(item.recipeId)) {
final Set<RecipeId> 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<RecipeId> getRecipeParents(RecipeChainMath math, RecipeId recipeId, Set<RecipeId> parents,
Set<RecipeId> visited) {
final Set<RecipeId> 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;
}
}
@@ -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<BookmarkItem> getBookmarkItems() {
final List<BookmarkItem> items = new ArrayList<>();
for (BookmarkItem item : this.grid.bookmarkItems) {
if (item.groupId == this.groupId) {
items.add(item);
}
}
return items;
}
}
@@ -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<Integer> 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<Integer, BookmarkChainItem> 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<Integer, RecipeId> 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<BookmarkItem> getBookmarkItems() {
final BookmarkGroup group = this.grid.getGroup(this.bookmarkItem.groupId);
final List<BookmarkItem> items = new ArrayList<>();
final List<RecipeId> 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);
}
}
@@ -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<ItemStack> items = ItemPanels.itemPanel.getItems();
final List<ItemStack> 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";
@@ -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<String> identifiers = preset.items;
final Set<String> 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<String> handleTooltip(int mousex, int mousey, List<String> 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);
}
}
@@ -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<PresetItemsGrid.PresetsGridSlot, ItemsGrid.MouseContext> {
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<ItemStack> newItems;
protected List<PresetsGridSlot> 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<PresetsGridSlot> 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();
@@ -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<String> identifiers = PresetsList.presets.stream().flatMap(p -> p.items.stream())
final AllMultiItemFilter andFilter = new AllMultiItemFilter();
final AnyMultiItemFilter orFilter = new AnyMultiItemFilter();
final Set<String> 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<String> handleTooltip(int mousex, int mousey, List<String> 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);
}
}
@@ -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;
@@ -294,7 +293,7 @@ public class GuiContainerManager {
}
private static int modelviewDepth = -1;
private static final HashSet<String> 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() {
@@ -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<ItemStack> 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<ItemStack> items,
ItemStack activeStack) {
return of(tooltipGUID, items, activeStack, DEFAULT_MAX_ROWS);
}
public static AcceptsFollowingTooltipLineHandler of(Object tooltipGUID, List<ItemStack> 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.drawMarker(x, y, 1f, "F", 0xA0A0A0, false, false);
}
}
}
@@ -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<BookmarkItem> 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<RecipeId, Long> recipes = iterator.next();
boolean craft = false;
for (Map.Entry<RecipeId, Long> 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<BookmarkItem> 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<BookmarkItem> prepareInitialItems(RecipeChainMath math, ItemStackAmount inventory) {
final List<BookmarkItem> 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();
}
}
@@ -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;
@@ -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<PositionedStack> ingredients = recipe.getIngredientStacks(recipeIndex);
List<DistributedIngred> 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<PositionedStack> ingredients = handler.getIngredientStacks(recipeIndex);
final List<DistributedIngred> ingredStacks = getPermutationIngredients(ingredients);
if (!clearIngredients(gui)) return 0;
findInventoryQuantities(gui, ingredStacks);
List<IngredientDistribution> assignedIngredients = assignIngredients(ingredients, ingredStacks);
if (assignedIngredients == null) return;
final List<IngredientDistribution> 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<PositionedStack> 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<IngredientDistribution> assignedIngredients, int quantity) {
private void moveIngredients(GuiContainer gui, List<IngredientDistribution> 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<IngredientDistribution> 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<DistributedIngred> 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
@@ -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;
@@ -20,17 +19,17 @@ public class FillFluidContainerHandler extends INEIGuiAdapter {
}
if (draggedStack.getItem() instanceof IFluidContainerItem || FluidContainerRegistry.isContainer(draggedStack)) {
ItemPanelSlot mouseOverSlot = ItemPanels.itemPanel.getSlotMouseOver(mouseX, mouseY);
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);
}
}
@@ -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<ICraftingHandler> {
@@ -33,7 +33,7 @@ public class GuiCraftingRecipe extends GuiRecipe<ICraftingHandler> {
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<ICraftingHandler> {
}
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<ICraftingHandler> {
return null;
}
public static boolean overlayRecipe(ItemStack stack, BookmarkRecipeId recipeId, final Boolean shift) {
if (stack == null || recipeId == null) {
return false;
}
final ArrayList<ICraftingHandler> 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<ICraftingHandler> getCraftingHandlers(String outputId, Object... results) {
ArrayList<ICraftingHandler> craftinghandlers = GuiCraftingRecipe.craftinghandlers;
ArrayList<ICraftingHandler> serialCraftingHandlers = GuiCraftingRecipe.serialCraftingHandlers;
@@ -91,9 +69,9 @@ public class GuiCraftingRecipe extends GuiRecipe<ICraftingHandler> {
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<ICraftingHandler> {
return stack;
}
protected static BookmarkRecipeId getRecipeId(GuiScreen gui, ItemStack stackover) {
public static RecipeId getRecipeId(GuiScreen gui, ItemStack stackover) {
if (gui instanceof GuiRecipe) {
final List<PositionedStack> 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<ICraftingHandler> handlers, boolean limitToOneRecipe) {
@@ -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<String, String> handleHotkeys(GuiContainer gui, int mousex, int mousey, Map<String, String> 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<String> handleTooltip(GuiRecipe<?> gui, List<String> 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<RecipeIngredient> results = new ArrayList<>(this.recipe.getResults());
if (results.size() <= 1) return true;
final Set<String> 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<Recipe> getRecipesTree(Recipe mainRecipe) {
int depth = NEIClientConfig.getIntSetting("inventory.favorites.depth");
final ItemStackSet items = new ItemStackSet();
final List<Recipe> result = new ArrayList<>();
List<Recipe> loop = new ArrayList<>();
loop.add(mainRecipe);
result.add(mainRecipe);
while (!loop.isEmpty() && depth-- >= 0) {
List<Recipe> localLoop = new ArrayList<>();
for (Recipe localRecipe : loop) {
for (RecipeIngredient ingr : localRecipe.getIngredients()) {
List<ItemStack> 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<ItemStack> permutations, ItemStack stack) {
for (int i = 0; i < permutations.size(); i++) {
if (StackInfo.equalItemAndNBT(stack, permutations.get(i), true)) {
return i;
}
}
return 0;
}
}
@@ -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<GuiOverlayButton> buttonList;
public UpdateOverlayButtonsEvent(GuiRecipe<?> gui, List<GuiOverlayButton> 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<GuiOverlayButton> 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<ItemOverlayState> 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<String> handleTooltip(GuiRecipe<?> gui, List<String> 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<String, String> handleHotkeys(GuiContainer gui, int mousex, int mousey, Map<String, String> 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<ItemOverlayState> ingredientsOverlay() {
List<PositionedStack> ingredients = this.handler.getIngredientStacks(recipeIndex);
List<PositionedStack> 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<ItemStack> 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<ItemOverlayState> presenceOverlay(List<PositionedStack> ingredients) {
final List<ItemOverlayState> itemPresenceSlots = new ArrayList<>();
final List<ItemStack> 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<PositionedStack> 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<PositionedStack> 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);
}
}
@@ -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<H extends IRecipeHandler> extends GuiContainer implements IGuiContainerOverlay,
IGuiClientSide, IGuiHandleMouseWheel, IContainerTooltipHandler, INEIGuiHandler {
@@ -67,14 +64,14 @@ public abstract class GuiRecipe<H extends IRecipeHandler> 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<IRecipeFilterProvider> 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<H extends IRecipeHandler> 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<H extends IRecipeHandler> extends GuiContainer i
private GuiButton nexttype;
private GuiButton prevtype;
// some mods reflect this property
@Deprecated
private GuiOverlayButton[] overlayButtons = new GuiOverlayButton[0];
private final List<GuiRecipeButton> 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<H extends IRecipeHandler> extends GuiContainer i
private int yShift = 0;
protected static class PermutationTooltipLineHandler extends ItemsTooltipLineHandler {
protected PositionedStack pStack = null;
public PermutationTooltipLineHandler(PositionedStack pStack, List<ItemStack> items) {
super(NEIClientUtils.translate("recipe.accepts"), items, false, 5);
this.pStack = pStack;
}
public static PermutationTooltipLineHandler getInstance(PositionedStack pStack) {
final List<ItemStack> 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<H extends IRecipeHandler> extends GuiContainer i
guiRecipe.changePage(0);
} else {
final IRecipeFilter filter = new ItemRecipeFilter(GuiRecipe.searchField.getFilter());
List<Integer> filtered = searchHandler.getSearchResult(filter);
final List<Integer> filtered = searchHandler.getSearchResult(filter);
if (filtered == null) {
stop();
@@ -323,9 +290,9 @@ public abstract class GuiRecipe<H extends IRecipeHandler> 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<H extends IRecipeHandler> 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<H extends IRecipeHandler> 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<H extends IRecipeHandler> 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<H extends IRecipeHandler> extends GuiContainer i
refreshPage();
}
private void updateOverlayButtons() {
private void updateRecipePage() {
final List<Integer> 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<GuiOverlayButton> buttons = new ArrayList<>();
List<GuiRecipeButton> buttons = new ArrayList<>();
if (MinecraftForge.EVENT_BUS.post(preEvent)) {
buttons = preEvent.buttonList;
@@ -517,42 +493,46 @@ public abstract class GuiRecipe<H extends IRecipeHandler> 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<Integer> 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<H extends IRecipeHandler> extends GuiContainer i
checkYShift();
}
public List<GuiButton> getOverlayButtons() {
return Collections.unmodifiableList(Arrays.asList(this.overlayButtons));
public List<GuiRecipeButton> getRecipeButtons() {
return Collections.unmodifiableList(this.recipeButtons);
}
public int openTargetRecipe(BookmarkRecipeId recipeId) {
public List<GuiButton> 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<H extends IRecipeHandler> 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,46 +626,19 @@ public abstract class GuiRecipe<H extends IRecipeHandler> extends GuiContainer i
return refIndex;
}
public List<PositionedStack> getFocusedRecipeIngredients() {
public Recipe getFocusedRecipe() {
List<Integer> 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<Integer> 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<PositionedStack> 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);
@@ -708,7 +668,7 @@ public abstract class GuiRecipe<H extends IRecipeHandler> extends GuiContainer i
final int recipesPerPage = getRecipesPerPage();
final int minIndex = page * recipesPerPage;
final int maxIndex = Math.min(this.numRecipes(), (page + 1) * recipesPerPage);
final ArrayList<Integer> range = new ArrayList<>();
final List<Integer> range = new ArrayList<>();
for (int index = minIndex; index < maxIndex; index++) {
range.add(handler.ref(index));
@@ -752,7 +712,7 @@ public abstract class GuiRecipe<H extends IRecipeHandler> 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 +774,19 @@ public abstract class GuiRecipe<H extends IRecipeHandler> 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 +815,17 @@ public abstract class GuiRecipe<H extends IRecipeHandler> 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 +836,7 @@ public abstract class GuiRecipe<H extends IRecipeHandler> 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 +871,36 @@ public abstract class GuiRecipe<H extends IRecipeHandler> 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<PositionedStack> stacks = handler.original.getIngredientStacks(recipeIndex);
if (itemstack != null && focused == null) {
final List<PositionedStack> 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 +910,21 @@ public abstract class GuiRecipe<H extends IRecipeHandler> extends GuiContainer i
public Map<String, String> handleHotkeys(GuiContainer gui, int mousex, int mousey, Map<String, String> 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 +949,6 @@ public abstract class GuiRecipe<H extends IRecipeHandler> 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 +964,6 @@ public abstract class GuiRecipe<H extends IRecipeHandler> 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 +977,19 @@ public abstract class GuiRecipe<H extends IRecipeHandler> 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 +999,7 @@ public abstract class GuiRecipe<H extends IRecipeHandler> 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 +1009,21 @@ public abstract class GuiRecipe<H extends IRecipeHandler> extends GuiContainer i
TemplateRecipeHandler.disableCycledIngredients = false;
List<PositionedStack> 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<PositionedStack> 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 +1078,12 @@ public abstract class GuiRecipe<H extends IRecipeHandler> 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 +1095,14 @@ public abstract class GuiRecipe<H extends IRecipeHandler> 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 +1122,16 @@ public abstract class GuiRecipe<H extends IRecipeHandler> 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();
@@ -1241,7 +1205,7 @@ public abstract class GuiRecipe<H extends IRecipeHandler> extends GuiContainer i
}
public boolean isMouseOver(PositionedStack stack, int refIndex) {
Point p = getRecipePosition(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);
@@ -1257,7 +1221,7 @@ public abstract class GuiRecipe<H extends IRecipeHandler> 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 +1230,14 @@ public abstract class GuiRecipe<H extends IRecipeHandler> 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 +1279,14 @@ public abstract class GuiRecipe<H extends IRecipeHandler> 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 GuiRecipe<?>gRecipe) && gRecipe.handler.numRecipes() > 0) {
final List<Integer> 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;
@@ -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<GuiRecipeButton> buttonList;
public UpdateRecipeButtonsEvent(GuiRecipe<?> gui, List<GuiRecipeButton> 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<GuiRecipeButton> 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<String> handleTooltip(GuiRecipe<?> gui, List<String> currenttip);
abstract Map<String, String> handleHotkeys(GuiContainer gui, int mousex, int mousey, Map<String, String> 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);
}
}
@@ -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<IUsageHandler> {
@@ -30,7 +31,7 @@ public class GuiUsageRecipe extends GuiRecipe<IUsageHandler> {
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);
@@ -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<ItemStack> ingredients;
private final ItemStack result;
protected RecipeId(ItemStack result, String handlerName, List<ItemStack> 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<ItemStack> 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<ItemStack> getIngredients() {
return new ArrayList<>(this.ingredients);
}
protected static List<ItemStack> extractItems(Iterable<?> items) {
final List<ItemStack> 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<PositionedStack> 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<ItemStack> items) {
final JsonArray arr = new JsonArray();
for (ItemStack nbTag : items) {
arr.add(NBTJson.toJsonObject(StackInfo.itemStackToNBT(nbTag)));
}
return arr;
}
protected static List<ItemStack> convertJsonArrayToItems(JsonArray arr) {
final List<ItemStack> 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<ItemStack> 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<ItemStack> 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<ItemStack> 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<ItemStack> 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<RecipeIngredient> ingredients, List<RecipeIngredient> 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<RecipeIngredient> ingredients = new ArrayList<>();
final List<RecipeIngredient> 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<RecipeIngredient> extractItems(List<?> items) {
final List<RecipeIngredient> 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<RecipeIngredient> getResults() {
return Arrays.asList(this.results);
}
public List<RecipeIngredient> 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<RecipeIngredient> ingredients = new ArrayList<>();
final List<RecipeIngredient> 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;
}
}
@@ -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<RecipeId, RecipeHandlerRef> 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<ICraftingHandler> 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<ItemOverlayState> 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;
}
}
}
@@ -1,17 +1,27 @@
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.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 +36,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 +57,56 @@ public class RecipeItemInputHandler implements IContainerInputHandler, IContaine
return hotkeys;
}
@Override
public List<String> handleItemTooltip(GuiContainer gui, ItemStack itemstack, int mousex, int mousey,
List<String> 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.getFavoriteRecipeTooltips() ? FavoriteRecipes.getFavorite(itemstack) : null;
}
return null;
}
@Override
public void onKeyTyped(GuiContainer gui, char keyChar, int keyID) {}
@@ -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<Slot> 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();
}
}
@@ -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<ItemStack> items = pStack.getFilteredPermutations();
final List<ItemStack> 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<ItemStack> items = stack.getFilteredPermutations();
final List<ItemStack> 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<ItemStack> 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<ItemStack> 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)) {
@@ -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<Map<RecipeId, Long>> {
public final ItemStackAmount inventory = new ItemStackAmount();
private final Set<RecipeId> precessedRecipes = new HashSet<>();
private RecipeChainMath math;
private Set<RecipeId> topRecipes;
private Set<RecipeId> processedRecipes;
private List<BookmarkItem> initialItems;
public RecipeChainIterator(RecipeChainMath math, List<BookmarkItem> 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<RecipeId, Long> next() {
refreshInitialItems(this.math, this.initialItems);
this.math.refresh();
final Set<RecipeId> skipRecipes = new HashSet<>();
final Map<RecipeId, Long> rootRecipes = new HashMap<>();
final HashMap<BookmarkItem, BookmarkItem> 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<BookmarkItem, BookmarkItem> 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<BookmarkItem> 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)));
}
}
}
}
@@ -0,0 +1,295 @@
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<RecipeId, Long> outputRecipes = new HashMap<>();
public final List<BookmarkItem> initialItems = new ArrayList<>();
public final List<BookmarkItem> recipeIngredients = new ArrayList<>();
public final List<BookmarkItem> recipeResults = new ArrayList<>();
public final Map<BookmarkItem, BookmarkItem> preferredItems = new HashMap<>();
public final Map<BookmarkItem, Long> requiredAmount = new HashMap<>();
private RecipeChainMath(List<BookmarkItem> recipeItems, Set<RecipeId> collapsedRecipes) {
final Map<RecipeId, Integer> recipeState = new HashMap<>();
final Map<RecipeId, Long> 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<RecipeId, Long> 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<BookmarkItem, BookmarkItem> maxReference = Collections.emptyMap();
RecipeId maxRecipeId = null;
int maxDepth = 0;
for (RecipeId recipeId : multipliers.keySet()) {
if (!this.outputRecipes.containsKey(recipeId) && this.preferredItems.values().stream()
.noneMatch(resItem -> resItem.recipeId.equals(recipeId))) {
final Map<BookmarkItem, BookmarkItem> references = new HashMap<>(this.preferredItems);
final int depth = collectPreferredItems(recipeId, references, new HashSet<>());
if (maxDepth < depth) {
maxReference = references;
maxRecipeId = recipeId;
maxDepth = depth;
}
}
}
if (!maxReference.isEmpty()) {
this.preferredItems.putAll(maxReference);
this.outputRecipes.put(maxRecipeId, multipliers.get(maxRecipeId));
} else {
break;
}
}
for (Map.Entry<RecipeId, Long> 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<RecipeId, Long> 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<BookmarkItem, BookmarkItem> preferredItems,
Set<RecipeId> 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)
&& !item.recipeId.equals(recipeId)
&& !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<BookmarkItem> chainItems, Set<RecipeId> collapsedRecipes) {
return new RecipeChainMath(chainItems, collapsedRecipes);
}
public static RecipeChainMath of(Recipe recipe, long multiplier) {
final List<BookmarkItem> 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<RecipeId> 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<RecipeId> 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;
}
}
}
}
@@ -0,0 +1,252 @@
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.ItemList;
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<BookmarkItem> 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<ItemStack> available = new ArrayList<>();
final List<ItemStack> inputs = new ArrayList<>();
final List<ItemStack> outputs = new ArrayList<>();
final List<ItemStack> 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()) {
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(stack -> ItemList.getItemOrderIndex(stack)));
outputs.sort(
Comparator.comparing((ItemStack stack) -> StackInfo.getFluid(stack) != null)
.thenComparing(stack -> ItemList.getItemOrderIndex(stack)));
remainder.sort(
Comparator.comparing((ItemStack stack) -> StackInfo.getFluid(stack) != null)
.thenComparing(stack -> ItemList.getItemOrderIndex(stack)));
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);
}
}
}
+58 -25
View File
@@ -21,29 +21,50 @@ 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.overlay_use=Fill Crafting Grid
nei.bookmark.group.sorting=Sorting
nei.bookmark.group.sorting.key=SHIFT + LMB + Drag
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.group.change_quantity=Change Quantity
nei.bookmark.group.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.remove_recipe=Remove Recipe
nei.itempanel.remove_item=Remove Item
nei.itempanel.add_item=Add Bookmark
nei.itempanel.add_item_with_recipe=Add Bookmark with Recipe
nei.itempanel.add_item_with_count=Add Bookmark with Count
nei.itempanel.change_quantity=Change Quantity
nei.itempanel.change_quantity_step=Change Quantity with Multiplier
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.sort_item=Sorting
nei.itempanel.sort_recipe=Sorting
nei.itempanel.copy_name=Copy Item Name
nei.itempanel.copy_oredict=Copy Item OreDictionary
nei.enchant=Enchant
nei.enchant.level=Level
@@ -145,16 +166,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 +199,35 @@ 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.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.showRecipeTooltip=Show Recipe Tooltips in Right Panel
nei.options.inventory.favorites.showRecipeTooltip.true=Yes
nei.options.inventory.favorites.showRecipeTooltip.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 +340,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 +483,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
+11 -23
View File
@@ -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 первыми
@@ -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=否
Binary file not shown.

Before

Width:  |  Height:  |  Size: 34 KiB

After

Width:  |  Height:  |  Size: 32 KiB