diff --git a/src/main/java/codechicken/nei/BookmarkPanel.java b/src/main/java/codechicken/nei/BookmarkPanel.java index d65a8efb..c6529ce3 100644 --- a/src/main/java/codechicken/nei/BookmarkPanel.java +++ b/src/main/java/codechicken/nei/BookmarkPanel.java @@ -19,6 +19,7 @@ import net.minecraft.client.gui.inventory.GuiContainer; import net.minecraft.entity.player.InventoryPlayer; import net.minecraft.item.ItemStack; import net.minecraft.nbt.NBTTagCompound; +import net.minecraft.util.EnumChatFormatting; import org.apache.commons.io.IOUtils; @@ -184,27 +185,33 @@ public class BookmarkPanel extends PanelWidget { if (itemStack != null && this.grid.indexOf(groupId, itemStack, recipeId, false) == -1) { final boolean existsRecipe = recipeId != null && this.grid.existsRecipe(recipeId, BookmarkGrid.DEFAULT_GROUP_ID); - final NBTTagCompound nbTag = StackInfo.itemStackToNBT(itemStack); - long count = nbTag.getInteger("Count"); - long factor = existsRecipe ? Math.max(count, 1) : 1; + final Recipe recipe = Recipe.of(recipeId); + final BookmarkItem.Builder builder; + + if (recipe != null) { + builder = BookmarkItem.builder(groupId, itemStack, recipe, BookmarkItemType.RESULT); + } else { + builder = BookmarkItem.builder(groupId, itemStack).recipeId(recipeId) + .factor(StackInfo.getAmount(itemStack)); + } + + builder.multiplier(1); if (existsRecipe) { + builder.type(BookmarkItemType.RESULT); + for (int i = 0; i < this.grid.size(); i++) { if (this.grid.getBookmarkItem(i).equalsRecipe(recipeId, groupId)) { - count *= this.grid.getBookmarkItem(i).getMultiplier(); + builder.multiplier(this.grid.getBookmarkItem(i).getMultiplier()); break; } } + + } else { + builder.type(BookmarkItemType.ITEM); } - this.grid.addItem( - BookmarkItem.of( - groupId, - StackInfo.withAmount(itemStack, count), - factor, - recipeId, - existsRecipe ? BookmarkItemType.RESULT : BookmarkItemType.ITEM), - true); + this.grid.addItem(builder.build(), true); return true; } @@ -212,9 +219,11 @@ public class BookmarkPanel extends PanelWidget { } public boolean addRecipe(Recipe recipe, int multiplier, int groupId) { + if (recipe == null) return false; + final RecipeId recipeId = recipe.getRecipeId(); - if (recipe != null && this.grid.getGroup(groupId) != null && !this.grid.existsRecipe(recipeId, groupId)) { + if (this.grid.getGroup(groupId) != null && !this.grid.existsRecipe(recipeId, groupId)) { this.grid.addRecipe(recipe, multiplier, groupId); return true; } @@ -245,15 +254,22 @@ public class BookmarkPanel extends PanelWidget { ItemStack stack = recipeId.getResult(); if (stack != null && uniqueRecipe.add(recipeId)) { - this.grid.addItem( - BookmarkItem - .of(groupId, stack, StackInfo.getAmount(stack), recipeId, BookmarkItemType.ITEM), - true); + final Recipe recipe = Recipe.of(recipeId); + final BookmarkItem.Builder builder; + + if (recipe != null) { + builder = BookmarkItem.builder(groupId, stack, recipe, BookmarkItemType.RESULT); + } else { + builder = BookmarkItem.builder(groupId, stack).recipeId(recipeId) + .factor(StackInfo.getAmount(stack)); + } + + this.grid.addItem(builder.multiplier(1).type(BookmarkItemType.ITEM).build(), true); uniqueStack.add(stack); } } else { - ItemStack stack = extractItem(item); + final ItemStack stack = extractItem(item); if (stack != null && !uniqueStack.contains(stack)) { this.grid.addItem(BookmarkItem.of(groupId, stack), true); @@ -925,6 +941,15 @@ public class BookmarkPanel extends PanelWidget { if (slot != null) { + if (slot.getBookmarkItem().chance != PositionedStack.CHANCE_FULL) { + currenttip.add( + 1, + EnumChatFormatting.DARK_GRAY + NEIClientUtils.translate( + "recipe.chance", + NEIClientUtils.formatNumber( + slot.getBookmarkItem().chance * 100f / PositionedStack.CHANCE_FULL))); + } + if (slot.getType() == BookmarkItemType.INGREDIENT && slot.getRecipeId() != null && slot.getBookmarkItem().permutations.size() > 1) { RecipeId recipeId = slot.getRecipeId(); @@ -1119,7 +1144,7 @@ public class BookmarkPanel extends PanelWidget { for (BookmarkItem item : math.initialItems) { final long invStackSize = inventory.getOrDefault(item.itemStack, 0L); - final long amount = item.amount - invStackSize * item.fluidCellAmount; + final long amount = item.getAmount() - invStackSize * item.fluidCellAmount; if (amount > 0) { pullItemStacks.add(item.getItemStack(amount)); } @@ -1133,7 +1158,7 @@ public class BookmarkPanel extends PanelWidget { for (BookmarkItem item : math.initialItems) { final long invStackSize = inventory.getOrDefault(item.itemStack, 0L); final long strStackSize = containerStorage.getOrDefault(item.itemStack, 0L); - final long amount = item.amount - invStackSize * item.fluidCellAmount + final long amount = item.getAmount() - invStackSize * item.fluidCellAmount - strStackSize * item.fluidCellAmount; if (amount > 0) { diff --git a/src/main/java/codechicken/nei/NEIClientConfig.java b/src/main/java/codechicken/nei/NEIClientConfig.java index 5c952ba2..72d46b27 100644 --- a/src/main/java/codechicken/nei/NEIClientConfig.java +++ b/src/main/java/codechicken/nei/NEIClientConfig.java @@ -266,6 +266,9 @@ public class NEIClientConfig { tag.getTag("inventory.guirecipe.profile").getBooleanValue(false); API.addOption(new OptionToggleButton("inventory.guirecipe.profile", true)); + tag.getTag("inventory.guirecipe.chance").getBooleanValue(false); + API.addOption(new OptionToggleButton("inventory.guirecipe.chance", true)); + tag.getTag("inventory.guirecipe.handlerInfo").setComment("ADVANCED: Handler Info Widget") .getBooleanValue(false); API.addOption(new OptionToggleButton("inventory.guirecipe.handlerInfo", true)); @@ -1140,6 +1143,10 @@ public class NEIClientConfig { return getBooleanSetting("inventory.guirecipe.shiftOverlayRecipe"); } + public static boolean showRecipeItemChances() { + return getBooleanSetting("inventory.guirecipe.chance"); + } + public static boolean isEnabled() { return enabledOverride && getBooleanSetting("inventory.widgetsenabled"); } diff --git a/src/main/java/codechicken/nei/NEIClientUtils.java b/src/main/java/codechicken/nei/NEIClientUtils.java index 12921bef..59ec14d2 100644 --- a/src/main/java/codechicken/nei/NEIClientUtils.java +++ b/src/main/java/codechicken/nei/NEIClientUtils.java @@ -126,7 +126,7 @@ public class NEIClientUtils extends NEIServerUtils { }); } - public static String formatNumber(long aNumber) { + public static String formatNumber(Number aNumber) { if (NEIModContainer.isGTNHLibLoaded()) { return NumberFormatUtil.formatNumber(aNumber); @@ -135,7 +135,7 @@ public class NEIClientUtils extends NEIServerUtils { return getDecimalFormat().format(aNumber); } - public static String formatFluid(long aNumber) { + public static String formatFluid(Number aNumber) { if (NEIModContainer.isGTNHLibLoaded()) { return NumberFormatUtil.formatFluid(aNumber); @@ -526,6 +526,11 @@ public class NEIClientUtils extends NEIServerUtils { } } + public static String getTextColorOrDefault(String key, String defaultColor) { + final String translated = NEIClientUtils.translate(key); // Optional localization string for resource packs + return translated.startsWith("nei.") ? defaultColor : translated; + } + public static void drawRect(double left, double top, double width, double height, Color color) { final boolean is2DTexture = GL11.glGetBoolean(GL11.GL_TEXTURE_2D); final boolean isBlend = GL11.glGetBoolean(GL11.GL_BLEND); diff --git a/src/main/java/codechicken/nei/PositionedStack.java b/src/main/java/codechicken/nei/PositionedStack.java index c2fe0d8b..53f26713 100644 --- a/src/main/java/codechicken/nei/PositionedStack.java +++ b/src/main/java/codechicken/nei/PositionedStack.java @@ -21,12 +21,16 @@ import codechicken.nei.recipe.StackInfo; */ public class PositionedStack { + /** Maximum chance value representing 100% probability (1 unit = 0.01%). */ + public static final int CHANCE_FULL = 10_000; + public int relx; public int rely; public ItemStack[] items; // compatibility dummy public ItemStack item; + protected int chance = CHANCE_FULL; private boolean permutated = false; public PositionedStack(Object object, int x, int y, boolean genPerms) { @@ -82,6 +86,10 @@ public class PositionedStack { for (ItemStack item : items) if (item.stackSize > i) item.stackSize = i; } + public int getChance() { + return this.chance; + } + public PositionedStack copy() { PositionedStack pStack = new PositionedStack( Arrays.stream(this.items).map(ItemStack::copy).toArray(ItemStack[]::new), @@ -89,6 +97,7 @@ public class PositionedStack { rely, false); pStack.permutated = this.permutated; + pStack.chance = getChance(); return pStack; } diff --git a/src/main/java/codechicken/nei/SubsetWidget.java b/src/main/java/codechicken/nei/SubsetWidget.java index 2060743f..72c2ed9b 100644 --- a/src/main/java/codechicken/nei/SubsetWidget.java +++ b/src/main/java/codechicken/nei/SubsetWidget.java @@ -694,13 +694,10 @@ public class SubsetWidget extends Button implements ItemFilterProvider { if (NEIClientConfig.subsetWidgetOnTop()) { return NEIClientUtils.translate("inventory.item_subsets"); } else { - final String translated = NEIClientUtils.translate("gui.button.label.subset"); - if (!translated.startsWith("nei.")) { // Optional localization string for resource packs - return translated; - } - - return EnumChatFormatting.DARK_PURPLE + String.valueOf(SearchField.searchParser.getRedefinedPrefix(PREFIX)) - + EnumChatFormatting.RESET; + return NEIClientUtils.getTextColorOrDefault( + "gui.button.label.subset", + EnumChatFormatting.DARK_PURPLE + + String.valueOf(SearchField.searchParser.getRedefinedPrefix(PREFIX))); } } diff --git a/src/main/java/codechicken/nei/api/ShortcutInputHandler.java b/src/main/java/codechicken/nei/api/ShortcutInputHandler.java index 0e4d11fa..fe70c8fa 100644 --- a/src/main/java/codechicken/nei/api/ShortcutInputHandler.java +++ b/src/main/java/codechicken/nei/api/ShortcutInputHandler.java @@ -23,6 +23,7 @@ import codechicken.nei.ItemsGrid.ItemsGridSlot; import codechicken.nei.LayoutManager; import codechicken.nei.NEIClientConfig; import codechicken.nei.NEIClientUtils; +import codechicken.nei.PositionedStack; import codechicken.nei.SearchField; import codechicken.nei.bookmark.BookmarkGrid; import codechicken.nei.bookmark.BookmarkGroup; @@ -646,8 +647,10 @@ public abstract class ShortcutInputHandler { final List playerInventory = AutoCraftingManager.getInventoryItems(guiContainer).values(); final RecipeId rootRecipeId = math.createMasterRoot(); - for (BookmarkItem item : math.recipeIngredients) { - if (item.amount > 0 && rootRecipeId.equals(item.recipeId)) { + for (int i = 0; i < math.recipeIngredients.size(); i++) { + final BookmarkItem item = math.recipeIngredients.get(i); + + if (item.getAmount() > 0 && rootRecipeId.equals(item.recipeId)) { long amount = 0; for (ItemStack stack : playerInventory) { @@ -656,8 +659,12 @@ public abstract class ShortcutInputHandler { } } - if (amount >= item.amount) { - item.factor = item.amount = amount + item.amount - (amount % item.amount); + if (amount >= item.getAmount()) { + math.recipeIngredients.set( + i, + BookmarkItem.builder(item).multiplier(1) + .factor(amount + item.getAmount() - (amount % item.getAmount())) + .chance(PositionedStack.CHANCE_FULL).build()); } } } diff --git a/src/main/java/codechicken/nei/bookmark/BookmarkGrid.java b/src/main/java/codechicken/nei/bookmark/BookmarkGrid.java index 8d5da5b4..98812065 100644 --- a/src/main/java/codechicken/nei/bookmark/BookmarkGrid.java +++ b/src/main/java/codechicken/nei/bookmark/BookmarkGrid.java @@ -24,7 +24,7 @@ import codechicken.lib.gui.GuiDraw; import codechicken.lib.vec.Rectangle4i; import codechicken.nei.BookmarkPanel.BookmarkViewMode; import codechicken.nei.ItemPanels; -import codechicken.nei.ItemStackAmount; +import codechicken.nei.ItemStackSet; import codechicken.nei.ItemsGrid; import codechicken.nei.ItemsGrid.MouseContext; import codechicken.nei.LayoutManager; @@ -33,6 +33,7 @@ import codechicken.nei.NEIClientUtils; import codechicken.nei.bookmark.BookmarkItem.BookmarkItemType; import codechicken.nei.recipe.Recipe; import codechicken.nei.recipe.Recipe.RecipeId; +import codechicken.nei.recipe.Recipe.RecipeIngredient; import codechicken.nei.recipe.StackInfo; import codechicken.nei.recipe.chain.RecipeChainMath; @@ -139,17 +140,16 @@ public class BookmarkGrid extends ItemsGrid results = recipe.getResults().stream().map(res -> res.getItemStack()) - .collect(Collectors.toList()); - final List ingredients = recipe.getIngredients().stream().map(ingr -> ingr.getItemStack()) - .collect(Collectors.toList()); + final ItemStackSet results = new ItemStackSet(); + final ItemStackSet ingredients = new ItemStackSet(); - for (ItemStack stack : ItemStackAmount.of(results).values()) { - long factor = StackInfo.getAmount(stack); + for (RecipeIngredient result : recipe.getResults()) { + results.add(result.getItemStack()); + } + + for (RecipeIngredient ingr : recipe.getIngredients()) { + ingredients.add(ingr.getItemStack()); + } + + for (ItemStack stack : results.values()) { this.addItem( - BookmarkItem.of( - groupId, - StackInfo.withAmount(stack, factor * multiplier), - factor, - recipeId, - BookmarkItemType.RESULT), + BookmarkItem.builder(groupId, stack, recipe, BookmarkItemType.RESULT).multiplier(multiplier) + .build(), true); } - for (ItemStack stack : ItemStackAmount.of(ingredients).values()) { - long factor = StackInfo.getAmount(stack); + for (ItemStack stack : ingredients.values()) { this.addItem( - BookmarkItem.of( - groupId, - StackInfo.withAmount(stack, factor * multiplier), - factor, - recipeId, - BookmarkItemType.INGREDIENT, - BookmarkItem.generatePermutations(stack, recipe)), + BookmarkItem.builder(groupId, stack, recipe, BookmarkItemType.INGREDIENT).multiplier(multiplier) + .build(), true); } } @@ -1133,8 +1128,8 @@ public class BookmarkGrid extends ItemsGrid sizes = new HashSet<>(); for (BookmarkItem item : this.bookmarkItems) { - if (item.groupId == targetGroupId && item.amount > 0) { - sizes.add(Math.max(item.getStackSize(), item.factor / item.fluidCellAmount)); + if (item.groupId == targetGroupId && item.getAmount() > 0) { + sizes.add(Math.max(item.getStackSize(), item.getStackSize(item.getAmount(1)))); items.add(item); } } @@ -1143,14 +1138,16 @@ public class BookmarkGrid extends ItemsGrid 0 ? Math.ceil(item.amount / (double) customFactor) : 0); + final long amount = item.getAmount(); + final long customFactor = (amount + gcd - 1) / gcd; + final long customMultiplier = customFactor > 0 ? ((amount + customFactor - 1) / customFactor) : 0; multiplier = Math.min(shiftMultiplier(customMultiplier, shift, 1), multiplier); } for (BookmarkItem item : items) { - long customFactor = (long) Math.ceil(item.amount / (double) gcd); - item.amount = customFactor * multiplier; + final long amount = item.getAmount(); + final long customFactor = (amount + gcd - 1) / gcd; + item.multiplier = item.getMultiplierFromAmount(customFactor * multiplier); } } else { final Map recipeMultipliers = new HashMap<>(); @@ -1158,7 +1155,7 @@ public class BookmarkGrid extends ItemsGrid items = new ArrayList<>(); for (BookmarkItem item : this.bookmarkItems) { - if (item.factor > 0 + if (!item.emptyFactor() && outputRecipes.stream().anyMatch(recipeId -> item.equalsRecipe(recipeId, targetGroupId))) { final long multiplier = recipeMultipliers.getOrDefault(item.recipeId, (long) Integer.MAX_VALUE); final boolean recipeInMiddle = group.crafting != null @@ -1168,7 +1165,7 @@ public class BookmarkGrid extends ItemsGrid 0) { + if (item.equalsRecipe(recipeId, targetItem.groupId) && !item.emptyFactor()) { multiplier = Math.min( shiftMultiplier( - Math.max(recipeInMiddle ? 1 : 0, item.getMultiplier()), + Math.max(recipeInMiddle ? 1 : 0, item.multiplier), shift, recipeInMiddle ? 1 : 0), multiplier); @@ -1208,8 +1205,8 @@ public class BookmarkGrid extends ItemsGrid 0) { - item.amount = item.factor * multiplier; + if (item.equalsRecipe(recipeId, targetItem.groupId) && !item.emptyFactor()) { + item.multiplier = multiplier; } } @@ -1217,7 +1214,7 @@ public class BookmarkGrid extends ItemsGrid fuzzyPermutations = new HashMap<>(); + + private final int groupId; + private final ItemStack stack; + private long factor; + private long multiplier = -1; + private long chance = PositionedStack.CHANCE_FULL; + private RecipeId recipeId = null; + private Recipe recipe = null; + private BookmarkItemType type = BookmarkItemType.ITEM; + private Map permutations = null; + + private Builder(int groupId, ItemStack stack) { + final NBTTagCompound nbTag = StackInfo.itemStackToNBT(stack); + + this.groupId = groupId; + this.stack = stack; + this.factor = nbTag.hasKey("gtFluidName") ? Math.min(144, nbTag.getInteger("Count")) : 1; + } + + public Builder factor(long factor) { + this.factor = factor; + return this; + } + + public Builder multiplier(long multiplier) { + this.multiplier = multiplier; + return this; + } + + public Builder chance(long chance) { + this.chance = chance; + return this; + } + + public Builder recipe(Recipe recipe) { + this.recipe = recipe; + this.recipeId = null; + return this; + } + + public Builder recipeId(RecipeId recipeId) { + this.recipeId = recipeId; + this.recipe = null; + return this; + } + + public Builder type(BookmarkItemType type) { + this.type = type; + return this; + } + + public Builder permutations(Map permutations) { + this.permutations = permutations; + return this; + } + + public BookmarkItem build() { + final Map perms; + + if (this.permutations != null) { + perms = this.permutations; + } else if (this.type == BookmarkItemType.INGREDIENT) { + + if (this.recipe != null) { + perms = Builder.generatePermutations(this.stack, this.recipe); + } else if (this.recipeId != null) { + perms = Builder.generatePermutations(this.stack, Recipe.of(this.recipeId)); + } else { + perms = Builder.generatePermutations(this.stack); + } + + } else { + perms = Builder.generatePermutations(this.stack); + } + + return new BookmarkItem( + this.groupId, + this.multiplier >= 0 ? this.multiplier : StackInfo.getAmount(this.stack) / this.factor, + this.factor, + this.chance, + this.stack, + perms, + this.recipeId != null ? this.recipeId : this.recipe != null ? this.recipe.getRecipeId() : null, + this.type); + } + + public static Map generatePermutations(ItemStack stack) { + return Collections.singletonMap(getItemGUID(stack), stack); + } + + public static Map generatePermutations(ItemStack stack, Recipe recipe) { + + if (recipe != null) { + final RecipeIngredient ingr = recipe.getIngredients().stream() + .filter(ingredient -> ingredient.contains(stack)).findAny().orElse(null); + + if (ingr != null) { + final Map permutations = new HashMap<>(); + for (ItemStack ingrStack : ingr.getPermutations()) { + permutations.put(getItemGUID(ingrStack), ingrStack); + } + return permutations; + } + } + + return generatePermutations(stack); + } + + private static synchronized String getItemGUID(ItemStack stack) { + final FluidStack fluidStack = StackInfo.getFluid(stack); + + if (fluidStack != null) { + return fluidStack.getFluid().getName() + ":" + fluidStack.tag; + } else { + + for (Map.Entry entry : Builder.fuzzyPermutations.entrySet()) { + if (NEIClientUtils.areStacksSameTypeCraftingWithNBT(stack, entry.getKey())) { + Builder.fuzzyPermutations.put(stack, entry.getValue()); + return entry.getValue(); + } + } + + final String stackGUID = StackInfo.getItemStackGUID(stack); + Builder.fuzzyPermutations.put(stack, stackGUID); + + return stackGUID; + } + } + + } + public enum BookmarkItemType { INGREDIENT, @@ -32,150 +168,128 @@ public class BookmarkItem { } } - private static Map fuzzyPermutations = new HashMap<>(); - public int groupId; - public long amount = 0L; - public final int fluidCellAmount; public ItemStack itemStack; public Map permutations; - public long factor; public RecipeId recipeId; public BookmarkItemType type = BookmarkItemType.ITEM; - protected BookmarkItem(int groupId, long amount, int fluidCellAmount, ItemStack itemStack, - Map permutations, long factor, RecipeId recipeId, BookmarkItemType type) { + public long multiplier = 1L; + public final long factor; + public final int fluidCellAmount; + public final long chance; + + protected BookmarkItem(int groupId, long multiplier, long factor, long chance, ItemStack itemStack, + Map permutations, RecipeId recipeId, BookmarkItemType type) { + final FluidStack fluidStack = StackInfo.getFluid(itemStack); + + if (fluidStack != null) { + this.fluidCellAmount = Math.max(1, StackInfo.isFluidContainer(itemStack) ? fluidStack.amount : 1); + } else { + this.fluidCellAmount = 1; + } + this.groupId = groupId; - this.amount = amount; - this.fluidCellAmount = fluidCellAmount; this.itemStack = itemStack; this.permutations = permutations; - this.factor = factor; this.recipeId = recipeId; this.type = type == null ? BookmarkItemType.ITEM : type; + + this.multiplier = multiplier; + this.factor = factor; + this.chance = chance; } - public static BookmarkItem of(int groupId, ItemStack stack, long factor, RecipeId recipeId, BookmarkItemType type, - Map permutations) { - final FluidStack fluidStack = StackInfo.getFluid(stack); - int fluidCellAmount = 1; - long amount; + public static Builder builder(int groupId, ItemStack stack) { + return new Builder(groupId, stack); + } - 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); + public static Builder builder(BookmarkItem item) { + return new Builder(item.groupId, item.itemStack).factor(item.factor).multiplier(item.multiplier) + .chance(item.chance).recipeId(item.recipeId).type(item.type).permutations(item.permutations); + } + + public static Builder builder(int groupId, ItemStack stack, Recipe recipe, BookmarkItemType type) { + final List items = type == BookmarkItemType.RESULT ? recipe.getResults() + : recipe.getIngredients(); + final Builder builder = builder(groupId, stack).type(type).recipe(recipe); + long amount = 0; + + for (RecipeIngredient res : items) { + if (res.contains(stack)) { + amount += res.getAmount() * res.getChance(); + } } - return new BookmarkItem( - groupId, - amount, - fluidCellAmount, - stack, - permutations, - factor * fluidCellAmount, - recipeId, - type); - } + if ((amount % PositionedStack.CHANCE_FULL) == 0) { + builder.factor(amount / PositionedStack.CHANCE_FULL); + } else { + builder.factor(1).chance(amount); + } - public static BookmarkItem of(int groupId, ItemStack stack, long factor, RecipeId recipeId, BookmarkItemType type) { - return of( - groupId, - stack, - factor, - recipeId, - type, - generatePermutations(stack, recipeId, type == BookmarkItemType.INGREDIENT)); + return builder; } 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, - BookmarkItemType.ITEM, - Collections.singletonMap(getItemGUID(stack), stack)); + return builder(groupId, stack).build(); } - public BookmarkItem copyWithAmount(long amount) { + public BookmarkItem copyWithMultiplier(long multiplier) { return new BookmarkItem( this.groupId, - amount, - this.fluidCellAmount, + multiplier, + this.factor, + this.chance, this.itemStack, this.permutations, - this.factor, this.recipeId, this.type); } + public BookmarkItem copyWithAmount(long amount) { + return copyWithMultiplier(getMultiplierFromAmount(amount)); + } + public BookmarkItem copy() { - return copyWithAmount(this.amount); - } - - public static Map generatePermutations(ItemStack stack, RecipeId recipeId, - boolean isIngredient) { - - if (isIngredient && recipeId != null) { - return generatePermutations(stack, Recipe.of(recipeId)); - } - - return Collections.singletonMap(getItemGUID(stack), stack); - } - - public static Map generatePermutations(ItemStack stack, Recipe recipe) { - - if (recipe != null) { - final RecipeIngredient ingr = recipe.getIngredients().stream() - .filter(ingredient -> ingredient.contains(stack)).findAny().orElse(null); - - if (ingr != null) { - final Map permutations = new HashMap<>(); - for (ItemStack ingrStack : ingr.getPermutations()) { - permutations.put(getItemGUID(ingrStack), ingrStack); - } - return permutations; - } - } - - return Collections.singletonMap(getItemGUID(stack), stack); - } - - private static synchronized String getItemGUID(ItemStack stack) { - final FluidStack fluidStack = StackInfo.getFluid(stack); - - if (fluidStack != null) { - return fluidStack.getFluid().getName() + ":" + fluidStack.tag; - } else { - - for (Map.Entry entry : BookmarkItem.fuzzyPermutations.entrySet()) { - if (NEIClientUtils.areStacksSameTypeCraftingWithNBT(stack, entry.getKey())) { - BookmarkItem.fuzzyPermutations.put(stack, entry.getValue()); - return entry.getValue(); - } - } - - final String stackGUID = StackInfo.getItemStackGUID(stack); - BookmarkItem.fuzzyPermutations.put(stack, stackGUID); - - return stackGUID; - } + return copyWithMultiplier(this.multiplier); } public boolean containsItems(BookmarkItem item) { return this.permutations.keySet().stream().anyMatch(item.permutations::containsKey); } + public long getAmount() { + return getAmount(this.multiplier); + } + + public long getAmount(long multiplier) { + long amount = this.factor * multiplier; + + if (this.fluidCellAmount > 1) { + amount = amount * this.fluidCellAmount; + } + + if (this.chance != PositionedStack.CHANCE_FULL) { + + if (this.type == BookmarkItemType.INGREDIENT) { + // For ingredients, round up the expected amount to be conservative. + amount = (amount * this.chance + PositionedStack.CHANCE_FULL - 1) / PositionedStack.CHANCE_FULL; + } else { + // For results, round down the expected amount to be conservative. + amount = (amount * this.chance) / PositionedStack.CHANCE_FULL; + } + + } + + return amount; + } + public ItemStack getItemStack() { - return getItemStack(this.amount); + return getItemStack(getAmount()); } public ItemStack getItemStack(long amount) { @@ -183,23 +297,34 @@ public class BookmarkItem { } public long getStackSize() { - return getStackSize(this.amount); + return getStackSize(getAmount()); } public long getStackSize(long amount) { - return (long) (this.factor > 0 ? Math.ceil(amount / (double) this.fluidCellAmount) : 0); + + if (this.fluidCellAmount > 1) { + amount = (amount + this.fluidCellAmount - 1) / this.fluidCellAmount; + } + + return amount; + } + + public long getMultiplierFromAmount(long amount) { + if (this.factor <= 0 || this.chance <= 0) return 0; + + if (this.fluidCellAmount > 1) { + amount = (amount + this.fluidCellAmount - 1) / this.fluidCellAmount; + } + + if (this.chance == PositionedStack.CHANCE_FULL) { + return (amount + this.factor - 1) / this.factor; + } + + return (amount * PositionedStack.CHANCE_FULL + this.factor * this.chance - 1) / (this.factor * this.chance); } 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; + return this.multiplier; } public boolean equalsRecipe(BookmarkItem meta) { @@ -210,6 +335,10 @@ public class BookmarkItem { return groupId == this.groupId && recipeId != null && recipeId.equals(this.recipeId); } + public boolean emptyFactor() { + return this.factor <= 0 || this.chance <= 0; + } + @Override public int hashCode() { return Objects.hash(this.groupId, this.fluidCellAmount, this.type.toInt(), this.recipeId); diff --git a/src/main/java/codechicken/nei/bookmark/BookmarkStorage.java b/src/main/java/codechicken/nei/bookmark/BookmarkStorage.java index b674cdb1..8dd05fbc 100644 --- a/src/main/java/codechicken/nei/bookmark/BookmarkStorage.java +++ b/src/main/java/codechicken/nei/bookmark/BookmarkStorage.java @@ -25,6 +25,7 @@ import com.google.gson.JsonSyntaxException; import codechicken.nei.BookmarkPanel.BookmarkViewMode; import codechicken.nei.NEIClientConfig; +import codechicken.nei.PositionedStack; import codechicken.nei.bookmark.BookmarkItem.BookmarkItemType; import codechicken.nei.recipe.Recipe.RecipeId; import codechicken.nei.recipe.StackInfo; @@ -249,30 +250,36 @@ public class BookmarkStorage { 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() + int groupId = jsonObject.has("groupId") && grid.groups.containsKey(jsonObject.get("groupId").getAsInt()) + ? jsonObject.get("groupId").getAsInt() : BookmarkGrid.DEFAULT_GROUP_ID; - int factor = jsonObject.has("factor") ? Math.abs(jsonObject.get("factor").getAsInt()) : (isFluid ? 144 : 1); - BookmarkItemType type = jsonObject.has("type") ? BookmarkItemType.fromInt(jsonObject.get("type").getAsInt()) - : null; + final BookmarkItem.Builder builder = BookmarkItem.builder(groupId, itemStack); - if (type == null) { - // old format - type = jsonObject.has("ingredient") && jsonObject.get("ingredient").getAsBoolean() - ? BookmarkItemType.INGREDIENT - : BookmarkItemType.RESULT; + if (jsonObject.has("type")) { + builder.type(BookmarkItemType.fromInt(jsonObject.get("type").getAsInt())); + } else if (jsonObject.has("ingredient") && jsonObject.get("ingredient").getAsBoolean()) { + builder.type(BookmarkItemType.INGREDIENT); + } else { + builder.type(BookmarkItemType.RESULT); + } + + if (jsonObject.has("multiplier")) { + builder.multiplier(jsonObject.get("multiplier").getAsInt()); + } + + if (jsonObject.has("factor")) { + builder.factor(jsonObject.get("factor").getAsInt()); + } + + if (jsonObject.has("chance")) { + builder.chance(jsonObject.get("chance").getAsInt()); } if (jsonObject.get("recipeId") instanceof JsonObject recipeJson) { - recipeId = RecipeId.of(recipeJson); + builder.recipeId(RecipeId.of(recipeJson)); } - if (!grid.groups.containsKey(groupId)) { - groupId = BookmarkGrid.DEFAULT_GROUP_ID; - } - - grid.addItem(BookmarkItem.of(groupId, itemStack, factor, recipeId, type), false); + grid.addItem(builder.build(), false); } return itemStack != null; @@ -321,7 +328,8 @@ public class BookmarkStorage { JsonObject row = new JsonObject(); row.add("item", NBTJson.toJsonObject(StackInfo.itemStackToNBT(item.getItemStack()))); - row.add("factor", new JsonPrimitive(item.getFactor())); + row.add("multiplier", new JsonPrimitive(item.multiplier)); + row.add("factor", new JsonPrimitive(item.factor)); row.add("type", new JsonPrimitive(item.type.toInt())); if (item.groupId != BookmarkGrid.DEFAULT_GROUP_ID) { @@ -332,6 +340,10 @@ public class BookmarkStorage { row.add("recipeId", item.recipeId.toJsonObject()); } + if (item.chance != PositionedStack.CHANCE_FULL) { + row.add("chance", new JsonPrimitive(item.chance)); + } + strings.add(NBTJson.toJson(row)); } catch (JsonSyntaxException e) { NEIClientConfig.logger.error("Failed to stringify bookmarked ItemStack to json string"); diff --git a/src/main/java/codechicken/nei/bookmark/BookmarksGridSlot.java b/src/main/java/codechicken/nei/bookmark/BookmarksGridSlot.java index 67bb2302..de46c3bd 100644 --- a/src/main/java/codechicken/nei/bookmark/BookmarksGridSlot.java +++ b/src/main/java/codechicken/nei/bookmark/BookmarksGridSlot.java @@ -14,6 +14,7 @@ import codechicken.nei.ItemsGrid.MouseContext; import codechicken.nei.NEIClientConfig; import codechicken.nei.NEIClientUtils; import codechicken.nei.NEIClientUtils.Alignment; +import codechicken.nei.PositionedStack; import codechicken.nei.bookmark.BookmarkGrid.BookmarkMouseContext; import codechicken.nei.bookmark.BookmarkItem.BookmarkItemType; import codechicken.nei.bookmark.RecipeChainDetails.CalculatedType; @@ -83,8 +84,8 @@ public class BookmarksGridSlot extends ItemsGridSlot { 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); + this.realMultiplier = bookmarkItem.getMultiplierFromAmount(realAmount); + this.calculatedMultiplier = bookmarkItem.getMultiplierFromAmount(calculatedAmount); if (this.group.crafting != null && this.group.crafting.calculatedItems.containsKey(this.itemIndex)) { this.realType = this.group.crafting.calculatedItems.get(this.itemIndex).calculatedType @@ -355,9 +356,10 @@ public class BookmarksGridSlot extends ItemsGridSlot { amount = this.shiftAmount; } - if (amount > 0 || shownItemType == ShownItemType.REAL && this.bookmarkItem.factor > 0) { + if (amount > 0 || shownItemType == ShownItemType.REAL && !this.bookmarkItem.emptyFactor()) { final float panelFactor = (rect.w - 2) / (DEFAULT_SLOT_SIZE - 2); final long stackSize = this.bookmarkItem.getStackSize(amount); + int color = 0xFFFFFF; String amountString = ""; if (stackSize < 10_000) { @@ -370,11 +372,16 @@ public class BookmarksGridSlot extends ItemsGridSlot { amountString += "L"; } + if (this.bookmarkItem.chance != PositionedStack.CHANCE_FULL) { + amountString = "~" + amountString; + color = 0xFFAA00; + } + NEIClientUtils.drawNEIOverlayText( amountString, new Rectangle4i(rect.x + 1, rect.y + 1, rect.w - 2, rect.h - 2), panelFactor, - 0xFFFFFF, + color, true, this.isFluidDisplay ? Alignment.BottomLeft : Alignment.BottomRight); } diff --git a/src/main/java/codechicken/nei/bookmark/RecipeChainDetails.java b/src/main/java/codechicken/nei/bookmark/RecipeChainDetails.java index e73a68de..45f84940 100644 --- a/src/main/java/codechicken/nei/bookmark/RecipeChainDetails.java +++ b/src/main/java/codechicken/nei/bookmark/RecipeChainDetails.java @@ -62,14 +62,15 @@ public class RecipeChainDetails { } public static BookmarkChainItem of(BookmarkItem item, long shiftItem, CalculatedType calculatedType) { - return new BookmarkChainItem(item, shiftItem, item.amount, calculatedType); + return new BookmarkChainItem(item, shiftItem, item.getAmount(), calculatedType); } public static BookmarkChainItem of(BookmarkItem item) { + final long amount = item.getAmount(); return new BookmarkChainItem( item, - item.amount, - item.amount, + amount, + amount, item.type == BookmarkItemType.INGREDIENT ? CalculatedType.INGREDIENT : CalculatedType.RESULT); } @@ -100,7 +101,7 @@ public class RecipeChainDetails { public void append(long amount, long calculatedAmount) { this.shiftAmount += amount; this.calculatedAmount += calculatedAmount; - this.item.amount = this.calculatedAmount; + this.item.multiplier = this.item.getMultiplierFromAmount(this.calculatedAmount); } } @@ -181,9 +182,9 @@ public class RecipeChainDetails { final BookmarkItem item = entry.getValue(); if (this.recipeInMiddle.contains(item.recipeId)) { this.calculatedItems.get(itemIndex) - .setRealAmount(Math.max(0, item.factor * (item.getMultiplier() - 1))); + .setRealAmount(Math.max(0, item.getAmount(item.getMultiplier() - 1))); } else { - this.calculatedItems.get(itemIndex).setRealAmount(item.amount); + this.calculatedItems.get(itemIndex).setRealAmount(item.getAmount()); } } } @@ -239,7 +240,7 @@ public class RecipeChainDetails { for (BookmarkItem item : chainItems) { if (recipeRelations.contains(item.recipeId)) { - subChainItems.add(item.copyWithAmount(recipeId.equals(item.recipeId) ? item.amount : 0)); + subChainItems.add(item.copyWithMultiplier(recipeId.equals(item.recipeId) ? item.multiplier : 0)); } } @@ -272,14 +273,14 @@ public class RecipeChainDetails { for (RecipeId relRecipeId : recipeRelations) { for (BookmarkItem item : math.recipeResults) { if (item.recipeId.equals(relRecipeId)) { - final long amount = item.amount - math.requiredAmount.getOrDefault(item, 0L); + final long amount = item.getAmount() - 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); + .append(amount, item.getAmount()); } else if (amount > 0) { items.computeIfAbsent( StackInfo.getItemStackGUID(item.itemStack), @@ -292,17 +293,17 @@ public class RecipeChainDetails { 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); + : math.requiredAmount.getOrDefault(item, item.getAmount()); 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) + if (amount != 0 || item.getAmount() > 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); + .append(amount, item.getAmount() - refAmount); } } } @@ -312,7 +313,7 @@ public class RecipeChainDetails { } private void generateResult(RecipeChainMath math, BookmarkItem item, int itemIndex) { - final long amount = item.amount - math.requiredAmount.getOrDefault(item, 0L); + final long amount = item.getAmount() - math.requiredAmount.getOrDefault(item, 0L); final CalculatedType calculatedType = math.outputRecipes.containsKey(item.recipeId) ? CalculatedType.RESULT : CalculatedType.REMAINDER; @@ -322,7 +323,7 @@ public class RecipeChainDetails { 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); + : math.requiredAmount.getOrDefault(item, item.getAmount()); this.calculatedItems.put(itemIndex, BookmarkChainItem.of(item, amount, CalculatedType.INGREDIENT)); } diff --git a/src/main/java/codechicken/nei/recipe/AutoCraftingManager.java b/src/main/java/codechicken/nei/recipe/AutoCraftingManager.java index 7c38dd5d..0717c3db 100644 --- a/src/main/java/codechicken/nei/recipe/AutoCraftingManager.java +++ b/src/main/java/codechicken/nei/recipe/AutoCraftingManager.java @@ -91,7 +91,7 @@ public class AutoCraftingManager { for (BookmarkItem item : math.initialItems) { final long invStackSize = inventory.getOrDefault(item.itemStack, 0L); - final long amount = Math.max(0, item.amount - invStackSize * item.fluidCellAmount); + final long amount = Math.max(0, item.getAmount() - invStackSize * item.fluidCellAmount); if (amount > 0) { initialItems.add(item.copyWithAmount(amount)); } diff --git a/src/main/java/codechicken/nei/recipe/GuiRecipe.java b/src/main/java/codechicken/nei/recipe/GuiRecipe.java index 771d54b5..78e54db1 100644 --- a/src/main/java/codechicken/nei/recipe/GuiRecipe.java +++ b/src/main/java/codechicken/nei/recipe/GuiRecipe.java @@ -899,12 +899,15 @@ public abstract class GuiRecipe extends GuiContainer i } private String getHandlerTitleColorCode(boolean hovered) { - final String key = hovered ? "recipe.title.color.hover" : "recipe.title.color.normal"; - final String translated = NEIClientUtils.translate(key); - if (!translated.startsWith("nei.")) { // Optional localization string for resource packs - return translated; + + if (hovered) { + return NEIClientUtils + .getTextColorOrDefault("recipe.title.color.hover", EnumChatFormatting.YELLOW.toString()); + } else { + return NEIClientUtils + .getTextColorOrDefault("recipe.title.color.normal", EnumChatFormatting.WHITE.toString()); } - return hovered ? EnumChatFormatting.YELLOW.toString() : EnumChatFormatting.WHITE.toString(); + } @Override diff --git a/src/main/java/codechicken/nei/recipe/NEIRecipeWidget.java b/src/main/java/codechicken/nei/recipe/NEIRecipeWidget.java index 972d2fdc..b88b1be7 100644 --- a/src/main/java/codechicken/nei/recipe/NEIRecipeWidget.java +++ b/src/main/java/codechicken/nei/recipe/NEIRecipeWidget.java @@ -1,6 +1,7 @@ package codechicken.nei.recipe; import java.awt.Point; +import java.text.DecimalFormat; import java.util.ArrayList; import java.util.Arrays; import java.util.Collections; @@ -21,9 +22,11 @@ import org.lwjgl.opengl.GL11; import org.lwjgl.opengl.GL12; import codechicken.lib.gui.GuiDraw; +import codechicken.lib.vec.Rectangle4i; import codechicken.nei.FavoriteRecipes; import codechicken.nei.NEIClientConfig; import codechicken.nei.NEIClientUtils; +import codechicken.nei.NEIClientUtils.Alignment; import codechicken.nei.NEIServerUtils; import codechicken.nei.PositionedStack; import codechicken.nei.Widget; @@ -37,9 +40,12 @@ import codechicken.nei.util.NEIMouseUtils; public class NEIRecipeWidget extends Widget { + private static final DecimalFormat chanceFormat = new DecimalFormat("##0.##%"); protected AcceptsFollowingTooltipLineHandler acceptsFollowingTooltipLineHandler; protected final Map favoriteIndexes = new WeakHashMap<>(); protected final Map> permutations = new WeakHashMap<>(); + protected final Map chances = new WeakHashMap<>(); + protected int chanceColor = 0xFDD835; protected int favoriteRevision = -1; protected boolean update = true; protected int cycleticks = 0; @@ -54,9 +60,20 @@ public class NEIRecipeWidget extends Widget { public NEIRecipeWidget(RecipeHandlerRef handlerRef) { this.handlerRef = handlerRef; this.handlerInfo = GuiRecipeTab.getHandlerInfo(this.handlerRef.handler); + this.chanceColor = getHexValue( + NEIClientUtils.getTextColorOrDefault("recipe.overlay.chance", "0xFDD835"), + this.chanceColor); update(); } + private int getHexValue(String color, int defaultValue) { + try { + return (int) Long.parseLong(color.replace("0x", ""), 16); + } catch (NumberFormatException e) { + return defaultValue; + } + } + public void setLocation(int x, int y) { this.x = x; this.y = y; @@ -196,11 +213,7 @@ public class NEIRecipeWidget extends Widget { updatePermutationsFor(pStack); } - GuiContainerManager.drawItem(pStack.relx, pStack.rely, pStack.item); - - if (pStack.contains(mouseX - this.x, mouseY - this.y - yShift)) { - NEIClientUtils.gl2DRenderContext(() -> GuiDraw.drawRect(pStack.relx, pStack.rely, 16, 16, 0x80FFFFFF)); - } + drawItem(pStack, mouseX, mouseY, yShift); } for (PositionedStack pStack : getCatalysts()) { @@ -209,19 +222,11 @@ public class NEIRecipeWidget extends Widget { updatePermutationsFor(pStack); } - GuiContainerManager.drawItem(pStack.relx, pStack.rely, pStack.item); - - if (pStack.contains(mouseX - this.x, mouseY - this.y - yShift)) { - NEIClientUtils.gl2DRenderContext(() -> GuiDraw.drawRect(pStack.relx, pStack.rely, 16, 16, 0x80FFFFFF)); - } + drawItem(pStack, mouseX, mouseY, yShift); } for (PositionedStack pStack : getOutputs()) { - GuiContainerManager.drawItem(pStack.relx, pStack.rely, pStack.item); - - if (pStack.contains(mouseX - this.x, mouseY - this.y - yShift)) { - NEIClientUtils.gl2DRenderContext(() -> GuiDraw.drawRect(pStack.relx, pStack.rely, 16, 16, 0x80FFFFFF)); - } + drawItem(pStack, mouseX, mouseY, yShift); } GuiContainerManager.disableMatrixStackLogging(); @@ -255,6 +260,41 @@ public class NEIRecipeWidget extends Widget { DebugHandlerWidget.instance.drawGuiPlaceholder(this); } + protected void drawItem(PositionedStack pStack, int mouseX, int mouseY, int yShift) { + GuiContainerManager.drawItem(pStack.relx, pStack.rely, pStack.item); + + if (NEIClientConfig.showRecipeItemChances()) { + final String chanceText = this.chances.computeIfAbsent(pStack, k -> getChanceText(k)); + + if (!chanceText.isEmpty()) { + NEIClientUtils.drawNEIOverlayText( + chanceText, + new Rectangle4i(pStack.relx, pStack.rely, 16, 16), + 0.5f, + this.chanceColor, + true, + Alignment.TopLeft); + } + } + + if (pStack.contains(mouseX - this.x, mouseY - this.y - yShift)) { + NEIClientUtils.gl2DRenderContext(() -> GuiDraw.drawRect(pStack.relx, pStack.rely, 16, 16, 0x80FFFFFF)); + } + } + + protected String getChanceText(PositionedStack pStack) { + + if (StackInfo.getAmount(pStack.item) == 0) { // not consumed + return NEIClientUtils.translate("recipe.chance.nc"); + } else if (pStack.getChance() == 0) { // not consumed parallel + return NEIClientUtils.translate("recipe.chance.ncp"); + } else if (pStack.getChance() != PositionedStack.CHANCE_FULL) { // chance based + return chanceFormat.format(pStack.getChance() / (float) PositionedStack.CHANCE_FULL); + } + + return ""; + } + @Override public boolean handleKeyPress(int keyID, char keyChar) { diff --git a/src/main/java/codechicken/nei/recipe/Recipe.java b/src/main/java/codechicken/nei/recipe/Recipe.java index 63c96e22..203cd3c7 100644 --- a/src/main/java/codechicken/nei/recipe/Recipe.java +++ b/src/main/java/codechicken/nei/recipe/Recipe.java @@ -282,17 +282,20 @@ public class Recipe { protected final ItemStack[] items; protected int activeIndex = 0; + protected int amount = 0; + protected int chance = PositionedStack.CHANCE_FULL; protected int relx; protected int rely; - public RecipeIngredient(int relx, int rely, List items, int activeIndex) { + public RecipeIngredient(int relx, int rely, List items, int chance, 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; + this.chance = chance; } public static RecipeIngredient of(PositionedStack positionedStack) { @@ -311,17 +314,21 @@ public class Recipe { } } - return of(positionedStack.relx, positionedStack.rely, stacks, activeIndex); + return of(positionedStack.relx, positionedStack.rely, stacks, positionedStack.getChance(), activeIndex); } - public static RecipeIngredient of(int relx, int rely, List stacks, int activeIndex) { - return new RecipeIngredient(relx, rely, stacks, activeIndex); + public static RecipeIngredient of(int relx, int rely, List stacks, int chance, int activeIndex) { + return new RecipeIngredient(relx, rely, stacks, chance, activeIndex); } public int getAmount() { return this.amount; } + public int getChance() { + return this.chance; + } + public ItemStack getItemStack() { return this.items[this.activeIndex]; } @@ -348,11 +355,16 @@ public class Recipe { return this; } + public void setChance(int chance) { + this.chance = chance; + } + public RecipeIngredient copy() { return new RecipeIngredient( this.relx, this.rely, getPermutations().stream().map(ItemStack::copy).collect(Collectors.toList()), + this.chance, this.activeIndex); } } @@ -435,7 +447,7 @@ public class Recipe { } if (item instanceof ItemStack stack) { - return RecipeIngredient.of(0, 0, Arrays.asList(stack), 0); + return RecipeIngredient.of(0, 0, Arrays.asList(stack), PositionedStack.CHANCE_FULL, 0); } if (item instanceof RecipeIngredient ingr) { diff --git a/src/main/java/codechicken/nei/recipe/chain/RecipeChainIterator.java b/src/main/java/codechicken/nei/recipe/chain/RecipeChainIterator.java index 960ae336..e1ccc942 100644 --- a/src/main/java/codechicken/nei/recipe/chain/RecipeChainIterator.java +++ b/src/main/java/codechicken/nei/recipe/chain/RecipeChainIterator.java @@ -49,9 +49,9 @@ public class RecipeChainIterator implements Iterator> { final Set skipRecipes = new HashSet<>(); final Map rootRecipes = new HashMap<>(); final HashMap preferredItems = new HashMap<>(this.math.preferredItems); - preferredItems.values() - .removeIf(item -> item == null || item.amount == 0 || this.precessedRecipes.contains(item.recipeId)); - preferredItems.keySet().removeIf(item -> item.amount == 0); + preferredItems.values().removeIf( + item -> item == null || item.getAmount() == 0 || this.precessedRecipes.contains(item.recipeId)); + preferredItems.keySet().removeIf(item -> item.getAmount() == 0); for (Map.Entry entry : preferredItems.entrySet()) { final BookmarkItem keyItem = entry.getKey(); diff --git a/src/main/java/codechicken/nei/recipe/chain/RecipeChainMath.java b/src/main/java/codechicken/nei/recipe/chain/RecipeChainMath.java index 17bf778c..3b87e167 100644 --- a/src/main/java/codechicken/nei/recipe/chain/RecipeChainMath.java +++ b/src/main/java/codechicken/nei/recipe/chain/RecipeChainMath.java @@ -14,6 +14,7 @@ import net.minecraft.item.ItemStack; import net.minecraft.nbt.NBTTagCompound; import codechicken.nei.ItemStackAmount; +import codechicken.nei.ItemStackSet; import codechicken.nei.NEIClientUtils; import codechicken.nei.bookmark.BookmarkItem; import codechicken.nei.bookmark.BookmarkItem.BookmarkItemType; @@ -48,6 +49,7 @@ public class RecipeChainMath { public final List recipeResults = new ArrayList<>(); public final Map preferredItems = new HashMap<>(); + public final Map requiredAmount = new HashMap<>(); public final List containerItems = new ArrayList<>(); private final List containerItemsBlacklist = new ArrayList<>(); @@ -59,9 +61,9 @@ public class RecipeChainMath { if (item.recipeId == null || item.type == BookmarkItemType.ITEM) { this.initialItems.add(item.copy()); } else if (item.type == BookmarkItemType.INGREDIENT) { - this.recipeIngredients.add(item.copyWithAmount(0)); + this.recipeIngredients.add(item.copyWithMultiplier(0)); } else { - this.recipeResults.add(item.copyWithAmount(0)); + this.recipeResults.add(item.copyWithMultiplier(0)); multipliers.put( item.recipeId, Math.max(multipliers.getOrDefault(item.recipeId, 0L), item.getMultiplier())); @@ -129,17 +131,18 @@ public class RecipeChainMath { visited.add(recipeId); for (BookmarkItem ingrItem : this.recipeIngredients) { - if (ingrItem.factor > 0 && recipeId.equals(ingrItem.recipeId) && !preferredItems.containsKey(ingrItem)) { + if (!ingrItem.emptyFactor() && recipeId.equals(ingrItem.recipeId) + && !preferredItems.containsKey(ingrItem)) { BookmarkItem activeItem = null; BookmarkItem prefItem = null; for (BookmarkItem item : this.recipeResults) { - if (item.factor > 0 && !visited.contains(item.recipeId) && item.containsItems(ingrItem)) { + if (!item.emptyFactor() && !visited.contains(item.recipeId) && item.containsItems(ingrItem)) { - if ((activeItem == null || item.factor > activeItem.factor) && NEIClientUtils + if ((activeItem == null || item.getAmount(1) > activeItem.getAmount(1)) && NEIClientUtils .areStacksSameTypeCraftingWithNBT(ingrItem.itemStack, item.itemStack)) { activeItem = item; - } else if (prefItem == null || item.factor > prefItem.factor) { + } else if (prefItem == null || item.getAmount(1) > prefItem.getAmount(1)) { prefItem = item; } @@ -164,7 +167,7 @@ public class RecipeChainMath { int maxDepth = 0; for (BookmarkItem ingrItem : preferredItems.keySet()) { - if (ingrItem.factor > 0 && recipeId.equals(ingrItem.recipeId)) { + if (!ingrItem.emptyFactor() && recipeId.equals(ingrItem.recipeId)) { maxDepth = Math.max(maxDepth, getMaxDepth(preferredItems.get(ingrItem).recipeId, preferredItems) + 1); } } @@ -176,7 +179,7 @@ public class RecipeChainMath { visited.add(recipeId); for (BookmarkItem ingrItem : this.recipeIngredients) { - if (ingrItem.factor > 0 && recipeId.equals(ingrItem.recipeId) && preferredItems.containsKey(ingrItem)) { + if (!ingrItem.emptyFactor() && recipeId.equals(ingrItem.recipeId) && preferredItems.containsKey(ingrItem)) { BookmarkItem prefItem = preferredItems.get(ingrItem); if (visited.contains(prefItem.recipeId)) { @@ -184,6 +187,7 @@ public class RecipeChainMath { } else { removeLoop(prefItem.recipeId, preferredItems, visited); } + } } @@ -195,22 +199,22 @@ public class RecipeChainMath { for (BookmarkItem item : this.recipeResults) { if (this.outputRecipes.containsKey(item.recipeId) && !ROOT_RECIPE_ID.equals(item.recipeId)) { - final long amount = item.factor * this.outputRecipes.get(item.recipeId); + final long multiplier = this.outputRecipes.get(item.recipeId); + final long amount = item.getAmount(multiplier); rootIngredients.add( - BookmarkItem.of( - -1, - item.getItemStack(amount), - item.getStackSize(amount), - ROOT_RECIPE_ID, - BookmarkItemType.INGREDIENT, - BookmarkItem.generatePermutations(item.itemStack, null))); + BookmarkItem.builder(-1, item.getItemStack(amount)).factor(item.getStackSize(amount)) + .permutations(BookmarkItem.Builder.generatePermutations(item.itemStack)) + .recipeId(ROOT_RECIPE_ID).type(BookmarkItemType.INGREDIENT).build()); + } } this.outputRecipes.clear(); this.outputRecipes.put(ROOT_RECIPE_ID, 1L); this.recipeResults.removeIf(item -> ROOT_RECIPE_ID.equals(item.recipeId)); - this.recipeResults.add(BookmarkItem.of(-1, ROOT_ITEM, 1, ROOT_RECIPE_ID, BookmarkItemType.RESULT)); + this.recipeResults.add( + BookmarkItem.builder(-1, ROOT_ITEM).factor(1).recipeId(ROOT_RECIPE_ID).type(BookmarkItemType.RESULT) + .build()); this.recipeIngredients.addAll(rootIngredients); return ROOT_RECIPE_ID; @@ -226,24 +230,20 @@ public class RecipeChainMath { public static RecipeChainMath of(Recipe recipe, long multiplier) { final List chainItems = new ArrayList<>(); - final RecipeId recipeId = recipe.getRecipeId(); final ItemStack result = recipe.getResult(); + final ItemStackSet ingredients = new ItemStackSet(); - chainItems.add(BookmarkItem.of(-1, result, StackInfo.getAmount(result), recipeId, BookmarkItemType.RESULT)); + chainItems + .add(BookmarkItem.builder(-1, result, recipe, BookmarkItemType.RESULT).multiplier(multiplier).build()); for (RecipeIngredient ingr : recipe.getIngredients()) { - chainItems.add( - BookmarkItem.of( - -1, - ingr.getItemStack(), - ingr.getAmount(), - recipeId, - BookmarkItemType.INGREDIENT, - BookmarkItem.generatePermutations(ingr.getItemStack(), recipe))); + ingredients.add(ingr.getItemStack()); } - for (BookmarkItem item : chainItems) { - item.amount *= multiplier; + for (ItemStack ingrStack : ingredients.values()) { + chainItems.add( + BookmarkItem.builder(-1, ingrStack, recipe, BookmarkItemType.INGREDIENT).multiplier(multiplier) + .build()); } return new RecipeChainMath(chainItems, Collections.emptySet()); @@ -253,7 +253,7 @@ public class RecipeChainMath { final ItemStackAmount missedItems = new ItemStackAmount(); for (BookmarkItem item : this.recipeResults) { - long amount = item.amount - this.requiredAmount.getOrDefault(item, 0L); + long amount = item.getAmount() - this.requiredAmount.getOrDefault(item, 0L); if (amount > 0) { missedItems.add(item.getItemStack(amount)); } @@ -261,7 +261,7 @@ public class RecipeChainMath { for (BookmarkItem item : this.recipeIngredients) { long amount = this.requiredAmount.containsKey(this.preferredItems.get(item)) ? 0 - : this.requiredAmount.getOrDefault(item, item.amount); + : this.requiredAmount.getOrDefault(item, item.getAmount()); if (amount > 0) { missedItems.add(item.getItemStack(amount)); } @@ -279,11 +279,11 @@ public class RecipeChainMath { private void resetCalculation() { for (BookmarkItem item : this.recipeIngredients) { - item.amount = 0; + item.multiplier = 0; } for (BookmarkItem item : this.recipeResults) { - item.amount = 0; + item.multiplier = 0; } this.preferredItems.clear(); @@ -313,8 +313,8 @@ public class RecipeChainMath { } for (BookmarkItem prefItem : this.recipeResults) { - if (prefItem.factor > 0 && this.outputRecipes.containsKey(prefItem.recipeId)) { - final long prefAmount = prefItem.factor * this.outputRecipes.get(prefItem.recipeId); + if (!prefItem.emptyFactor() && this.outputRecipes.containsKey(prefItem.recipeId)) { + final long prefAmount = prefItem.getAmount(this.outputRecipes.get(prefItem.recipeId)); if (prefItem.itemStack.getItem().hasContainerItem(prefItem.itemStack)) { this.containerItemsBlacklist.add(prefItem.itemStack); @@ -327,9 +327,9 @@ public class RecipeChainMath { } for (BookmarkItem prefItem : this.recipeResults) { - if (prefItem.factor > 0 && this.outputRecipes.containsKey(prefItem.recipeId) + if (!prefItem.emptyFactor() && this.outputRecipes.containsKey(prefItem.recipeId) && this.requiredAmount.containsKey(prefItem)) { - final long prefAmount = prefItem.factor * this.outputRecipes.get(prefItem.recipeId); + final long prefAmount = prefItem.getAmount(this.outputRecipes.get(prefItem.recipeId)); this.requiredAmount.put(prefItem, this.requiredAmount.get(prefItem) - prefAmount); } } @@ -338,14 +338,6 @@ public class RecipeChainMath { return this; } - private void prepareIngredients(RecipeId recipeId, long stepShift, List visited) { - for (BookmarkItem item : this.recipeIngredients) { - if (item.factor > 0 && recipeId.equals(item.recipeId)) { - calculateSuitableRecipe(item, item.factor * stepShift, visited); - } - } - } - private void calculateSuitableRecipe(BookmarkItem ingrItem, long ingrAmount, List visited) { final BookmarkItem prefItem = this.preferredItems.get(ingrItem); @@ -366,7 +358,7 @@ public class RecipeChainMath { if (ingrAmount > 0) { for (BookmarkItem item : this.initialItems) { if (item.containsItems(ingrItem) - && (ingrAmount = addRequiredAmount(item, ingrAmount, item.amount)) == 0) { + && (ingrAmount = addRequiredAmount(item, ingrAmount, item.getAmount())) == 0) { break; } } @@ -379,19 +371,27 @@ public class RecipeChainMath { addRequiredAmount(prefItem, ingrAmount, Long.MAX_VALUE); } else { addRequiredAmount(prefItem, ingrAmount, Long.MAX_VALUE); - final long shift = (long) Math - .ceil((this.requiredAmount.get(prefItem) - prefItem.amount) / (double) prefItem.factor); + final long multiplier = prefItem + .getMultiplierFromAmount(this.requiredAmount.get(prefItem) - prefItem.getAmount()); - if (shift > 0) { - addShift(prefItem.recipeId, shift); + if (multiplier > 0) { + addShift(prefItem.recipeId, multiplier); visited.add(prefItem.recipeId); - prepareIngredients(prefItem.recipeId, shift, visited); + prepareIngredients(prefItem.recipeId, multiplier, visited); visited.remove(prefItem.recipeId); } } } + private void prepareIngredients(RecipeId recipeId, long multiplier, List visited) { + for (BookmarkItem item : this.recipeIngredients) { + if (!item.emptyFactor() && recipeId.equals(item.recipeId)) { + calculateSuitableRecipe(item, item.getAmount(multiplier), visited); + } + } + } + private long addRequiredAmount(BookmarkItem prefItem, long ingrAmount, long maxAmount) { long shiftAmount = this.requiredAmount.getOrDefault(prefItem, 0L); @@ -489,7 +489,7 @@ public class RecipeChainMath { final NBTTagCompound toolStats = tagCompound.getCompoundTag("GT.ToolStats"); final long maxDamage = toolStats.getLong("MaxDamage"); final long damage = toolStats.getLong("Damage"); - final long leftSteps = (long) Math.ceil((maxDamage - damage) / (double) damagePerContainerCraft); + final long leftSteps = (maxDamage - damage + damagePerContainerCraft - 1) / damagePerContainerCraft; final long availableSteps = Math.min(steps, Math.max(1, leftSteps)); steps -= availableSteps; @@ -552,13 +552,13 @@ public class RecipeChainMath { private void addShift(RecipeId recipeId, long shift) { for (BookmarkItem item : this.recipeIngredients) { if (recipeId.equals(item.recipeId)) { - item.amount += item.factor * shift; + item.multiplier += shift; } } for (BookmarkItem item : this.recipeResults) { if (recipeId.equals(item.recipeId)) { - item.amount += item.factor * shift; + item.multiplier += shift; } } } diff --git a/src/main/java/codechicken/nei/recipe/chain/RecipeChainTooltipLineHandler.java b/src/main/java/codechicken/nei/recipe/chain/RecipeChainTooltipLineHandler.java index 1f2f4af3..abcb9df8 100644 --- a/src/main/java/codechicken/nei/recipe/chain/RecipeChainTooltipLineHandler.java +++ b/src/main/java/codechicken/nei/recipe/chain/RecipeChainTooltipLineHandler.java @@ -73,7 +73,7 @@ public class RecipeChainTooltipLineHandler implements ITooltipLineHandler { if (!this.lastControlKey) { final List items = inventory.values(); for (BookmarkItem item : math.recipeResults) { - if (item.factor > 0 && this.math.outputRecipes.containsKey(item.recipeId)) { + if (!item.emptyFactor() && this.math.outputRecipes.containsKey(item.recipeId)) { long amount = 0; for (ItemStack stack : items) { @@ -83,15 +83,18 @@ public class RecipeChainTooltipLineHandler implements ITooltipLineHandler { } } - if (amount >= item.amount) { - final long itemAmount = item.factor * this.math.outputRecipes.get(item.recipeId); + if (amount >= item.getAmount()) { + final long itemAmount = item.getAmount(this.math.outputRecipes.get(item.recipeId)); + if (itemAmount > 0) { amount += itemAmount - amount % itemAmount; } this.math.outputRecipes.put( item.recipeId, - Math.max(this.math.outputRecipes.get(item.recipeId), amount / item.factor)); + Math.max( + this.math.outputRecipes.get(item.recipeId), + item.getMultiplierFromAmount(amount))); } } } @@ -122,7 +125,7 @@ public class RecipeChainTooltipLineHandler implements ITooltipLineHandler { for (BookmarkItem item : math.recipeIngredients) { final long amount = math.requiredAmount.containsKey(math.preferredItems.get(item)) ? 0 - : math.requiredAmount.getOrDefault(item, item.amount); + : math.requiredAmount.getOrDefault(item, item.getAmount()); if (amount > 0) { inputs.add(item.getItemStack(amount)); @@ -130,7 +133,7 @@ public class RecipeChainTooltipLineHandler implements ITooltipLineHandler { } for (BookmarkItem item : math.recipeResults) { - final long amount = item.amount - math.requiredAmount.getOrDefault(item, 0L); + final long amount = item.getAmount() - math.requiredAmount.getOrDefault(item, 0L); if (amount > 0) { if (math.outputRecipes.containsKey(item.recipeId)) { @@ -155,12 +158,12 @@ public class RecipeChainTooltipLineHandler implements ITooltipLineHandler { 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 ((item.getAmount() - invAmount) > 0) { + inputs.add(item.getItemStack(item.getAmount() - invAmount)); } - if (Math.min(item.amount, invAmount) > 0) { - available.add(item.getItemStack(Math.min(item.amount, invAmount))); + if (Math.min(item.getAmount(), invAmount) > 0) { + available.add(item.getItemStack(Math.min(item.getAmount(), invAmount))); } } else { diff --git a/src/main/resources/assets/nei/lang/en_US.lang b/src/main/resources/assets/nei/lang/en_US.lang index bb3f98b0..1c0df135 100644 --- a/src/main/resources/assets/nei/lang/en_US.lang +++ b/src/main/resources/assets/nei/lang/en_US.lang @@ -302,6 +302,9 @@ nei.options.inventory.guirecipe.handlerInfo.tip=Use shift+d while in the Recipes nei.options.inventory.guirecipe.handlerInfo.true=Enabled nei.options.inventory.guirecipe.handlerInfo.false=Disabled nei.options.inventory.guirecipe.maxHeight=Max Height +nei.options.inventory.guirecipe.chance=Show Change +nei.options.inventory.guirecipe.chance.true=Yes +nei.options.inventory.guirecipe.chance.false=No nei.options.inventory.showItemQuantityWidget=Show Item Quantity Widget nei.options.inventory.showItemQuantityWidget.true=Yes nei.options.inventory.showItemQuantityWidget.false=No @@ -557,6 +560,10 @@ nei.recipe.shapeless=Shapeless Crafting nei.recipe.furnace=Smelting nei.recipe.fuel=Fuel nei.recipe.fuel.smeltCount=Smelts %s items +nei.recipe.chance=Chance: %s%% +nei.recipe.chance.nc=NC +nei.recipe.chance.ncp=NC(P) + # Deprecated nei.recipe.fuel.required=%s required # Deprecated