Compare commits

...
Author SHA1 Message Date
Martin Robertz 5576f1ba09 Merge remote-tracking branch 'slprime/bookmarks_groups_and_filter' into dev 2024-02-03 16:33:27 +01:00
slprime d64c270e3a Bookmarks Recipe Chains: fluid cell 2024-02-03 15:33:01 +02:00
Martin Robertz 12da0ba608 Merge remote-tracking branch 'slprime/bookmarks_groups_and_filter' into dev 2024-02-02 15:36:55 +01:00
slprime 3d07a33e3c Bookmarks Recipe Chains: fluid canner 2024-02-01 20:18:44 +02:00
Martin Robertz 4062f6c001 update bs 2024-01-31 23:18:49 +01:00
Martin Robertz 8fbb876a81 Merge remote-tracking branch 'slprime/bookmarks_groups_and_filter' into dev 2024-01-31 23:08:47 +01:00
slprime e050faaa90 fix fast DnD and recipe tooltip 2024-01-29 21:07:18 +02:00
Martin Robertz 576da1468c Merge remote-tracking branch 'slprime/bookmarks_groups_and_filter' into dev 2024-01-29 07:19:44 +01:00
slprime 316494df15 fix creating groups for 2-line recipes 2024-01-28 18:20:41 +02:00
Martin Robertz 66deab6dbd Merge remote-tracking branch 'slprime/bookmarks_groups_and_filter' into dev 2024-01-26 21:30:09 +01:00
slprime 4953edde4b fixed rounding in chain calculation 2024-01-26 19:19:02 +02:00
slprime 9873de760f Change Search Recipe Algorithm 2024-01-26 17:53:31 +02:00
Martin Robertz 355cc5bd0e Merge remote-tracking branch 'slprime/bookmarks_groups_and_filter' into dev 2024-01-22 18:21:53 +01:00
slprime 183e40a6c2 fix recipe extra information 2024-01-22 00:57:27 +02:00
Martin Robertz 79660a09d5 Merge remote-tracking branch 'slprime/bookmarks_groups_and_filter' into dev 2024-01-21 11:16:56 +01:00
slprime b03ae51e3c Add Search Recipe Icon 2024-01-21 12:11:49 +02:00
Martin Robertz 91d57c28b7 Merge remote-tracking branch 'slprime/bookmarks_groups_and_filter' into dev 2024-01-20 21:43:51 +01:00
slprime 06cdea3fd6 fix input V in ME terminal 2024-01-19 23:39:01 +02:00
Martin Robertz ceae486a31 update bs+properties 2024-01-16 22:10:41 +01:00
Martin Robertz d35e77661f Merge remote-tracking branch 'slprime/bookmarks_groups_and_filter' into dev 2024-01-16 22:09:20 +01:00
slprime 4285632807 remove println 2024-01-14 16:02:21 +02:00
slprime b16916dbe6 Optimize Chain Algorithm 2024-01-14 14:51:26 +02:00
Martin Robertz 57db3fa789 Merge remote-tracking branch 'slprime/bookmarks_groups_and_filter' into dev 2024-01-14 10:38:18 +01:00
slprime c5c798c61a Pull all Bookmarked Items: use groups 2024-01-13 18:35:19 +02:00
Martin Robertz f9cba79a22 update deps 2024-01-13 00:09:15 +01:00
slprime 0a24334693 change hiddenhandler handlerId 2024-01-11 23:43:36 +02:00
slprime 8ef88ff9f2 fix crash after DnD; add remove group function 2024-01-11 21:08:08 +02:00
slprime 4cfbe6b3ad Create Bookmarks Groups; Search Recipe 2024-01-10 23:29:03 +02:00
41 changed files with 3131 additions and 892 deletions

No files matched your search

+24 -5
View File
@@ -1,4 +1,4 @@
//version: 1704751096
//version: 1706545953
/*
DO NOT CHANGE THIS FILE!
Also, you may replace this file at any time if there is an update available.
@@ -122,6 +122,7 @@ propertyDefaultIfUnset("modrinthProjectId", "")
propertyDefaultIfUnset("modrinthRelations", "")
propertyDefaultIfUnset("curseForgeProjectId", "")
propertyDefaultIfUnset("curseForgeRelations", "")
propertyDefaultIfUnset("versionPattern", "")
propertyDefaultIfUnset("minimizeShadowedDependencies", true)
propertyDefaultIfUnset("relocateShadowedDependencies", true)
// Deprecated properties (kept for backwards compat)
@@ -370,6 +371,7 @@ catch (Exception ignored) {
// Pulls version first from the VERSION env and then git tag
String identifiedVersion
String versionOverride = System.getenv("VERSION") ?: null
boolean checkVersion = false
try {
// Produce a version based on the tag, or for branches something like 0.2.2-configurable-maven-and-extras.38+43090270b6-dirty
if (versionOverride == null) {
@@ -388,6 +390,8 @@ try {
}
} else if (isDirty) {
identifiedVersion += "-${branchName}+${gitDetails.gitHash}-dirty"
} else {
checkVersion = true
}
} else {
identifiedVersion = versionOverride
@@ -409,6 +413,8 @@ ext {
if (identifiedVersion == versionOverride) {
out.style(Style.Failure).text('Override version to ').style(Style.Identifier).text(modVersion).style(Style.Failure).println('!\7')
} else if (checkVersion && versionPattern && !(identifiedVersion ==~ versionPattern)) {
throw new GradleException("Invalid version: '$identifiedVersion' does not match version pattern '$versionPattern'")
}
group = "com.github.GTNewHorizons"
@@ -428,18 +434,31 @@ minecraft {
for (f in replaceGradleTokenInFile.split(',')) {
tagReplacementFiles.add f
}
out.style(Style.Info).text('replaceGradleTokenInFile is deprecated! Consider using generateGradleTokenClass.').println()
}
if (gradleTokenModId) {
injectedTags.put gradleTokenModId, modId
if (replaceGradleTokenInFile) {
injectedTags.put gradleTokenModId, modId
} else {
out.style(Style.Failure).text('gradleTokenModId is deprecated! The field will no longer be generated.').println()
}
}
if (gradleTokenModName) {
injectedTags.put gradleTokenModName, modName
if (replaceGradleTokenInFile) {
injectedTags.put gradleTokenModName, modName
} else {
out.style(Style.Failure).text('gradleTokenModName is deprecated! The field will no longer be generated.').println()
}
}
if (gradleTokenVersion) {
injectedTags.put gradleTokenVersion, modVersion
}
if (gradleTokenGroupName) {
injectedTags.put gradleTokenGroupName, modGroup
if (replaceGradleTokenInFile) {
injectedTags.put gradleTokenGroupName, modGroup
} else {
out.style(Style.Failure).text('gradleTokenGroupName is deprecated! The field will no longer be generated.').println()
}
}
if (enableGenericInjection.toBoolean()) {
injectMissingGenerics.set(true)
@@ -632,7 +651,7 @@ repositories {
def mixinProviderGroup = "io.github.legacymoddingmc"
def mixinProviderModule = "unimixins"
def mixinProviderVersion = "0.1.13"
def mixinProviderVersion = "0.1.15"
def mixinProviderSpecNoClassifer = "${mixinProviderGroup}:${mixinProviderModule}:${mixinProviderVersion}"
def mixinProviderSpec = "${mixinProviderSpecNoClassifer}:dev"
ext.mixinProviderSpec = mixinProviderSpec
+2 -2
View File
@@ -1,7 +1,7 @@
// Add your dependencies here
dependencies {
api('com.github.GTNewHorizons:CodeChickenLib:1.2.0:dev')
api('com.github.GTNewHorizons:CodeChickenCore:1.2.0:dev')
api('com.github.GTNewHorizons:CodeChickenLib:1.2.1:dev')
api('com.github.GTNewHorizons:CodeChickenCore:1.2.1:dev')
shadowImplementation('org.apache.commons:commons-csv:1.10.0')
}
+3 -3
View File
@@ -28,10 +28,10 @@ developmentEnvironmentUserName = Developer
# Leave these properties empty to skip individual token replacements
# Generate a class with String fields for the mod id, name, version and group name named with the fields below
generateGradleTokenClass = codechicken.nei.Tags
gradleTokenModId = MODID
gradleTokenModName = MODNAME
gradleTokenModId =
gradleTokenModName =
gradleTokenVersion = VERSION
gradleTokenGroupName = GROUPNAME
gradleTokenGroupName =
# [DEPRECATED]
# Multiple source files can be defined here by providing a comma-seperated list: Class1.java,Class2.java,Class3.java
# public static final String VERSION = "GRADLETOKEN_VERSION";
@@ -0,0 +1,315 @@
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> 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.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 iCount = calculateCount(request, request.inputs.get(item.stackIndex))
- iShift.getOrDefault(item.stackIndex, 0L);
this.multiplier.put(item.stack, item.factor > 0 ? (int) (count / item.factor) : 0);
if (iCount < count) {
this.outputs.put(item.stack, item.getItemStack(count - iCount));
iShift.put(item.stackIndex, iShift.getOrDefault(item.stackIndex, 0L) + iCount);
} else if (iCount >= count) {
this.intermediate.put(item.stack, item.getItemStack(0));
iShift.put(item.stackIndex, iShift.getOrDefault(item.stackIndex, 0L) + count);
}
}
}
}
}
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
@@ -28,6 +28,7 @@ import net.minecraft.entity.item.EntityItem;
import net.minecraft.world.World;
import net.minecraftforge.client.event.RenderWorldLastEvent;
import net.minecraftforge.common.MinecraftForge;
import net.minecraftforge.event.world.WorldEvent;
import org.apache.commons.csv.CSVFormat;
import org.apache.commons.csv.CSVParser;
@@ -48,6 +49,8 @@ import cpw.mods.fml.common.FMLCommonHandler;
import cpw.mods.fml.common.eventhandler.SubscribeEvent;
import cpw.mods.fml.common.gameevent.TickEvent;
import cpw.mods.fml.common.gameevent.TickEvent.Phase;
import cpw.mods.fml.relauncher.Side;
import cpw.mods.fml.relauncher.SideOnly;
public class ClientHandler {
@@ -145,6 +148,7 @@ public class ClientHandler {
public static void preInit() {
loadSerialHandlers();
loadHeightHackHandlers();
loadHiddenHandlers();
ItemInfo.preInit();
StackInfo.loadGuidFilters();
}
@@ -197,6 +201,30 @@ public class ClientHandler {
}
}
public static void loadHiddenHandlers() {
File file = NEIClientConfig.hiddenHandlersFile;
if (!file.exists()) {
try (FileWriter writer = new FileWriter(file)) {
NEIClientConfig.logger.info("Creating default hidden handlers list {}", file);
URL defaultHeightHackHandlersResource = ClientHandler.class
.getResource("/assets/nei/cfg/hiddenhandlers.cfg");
if (defaultHeightHackHandlersResource != null) {
IOUtils.copy(defaultHeightHackHandlersResource.openStream(), writer);
}
} catch (IOException e) {
NEIClientConfig.logger.error("Failed to save default hidden handlers list to file {}", file, e);
}
}
try (FileReader reader = new FileReader(file)) {
NEIClientConfig.logger.info("Loading hidden handlers from file {}", file);
NEIClientConfig.hiddenHandlers = IOUtils.readLines(reader).stream().filter((line) -> !line.startsWith("#"))
.collect(Collectors.toCollection(HashSet::new));
} catch (IOException e) {
NEIClientConfig.logger.error("Failed to load hidden handlers from file {}", file, e);
}
}
public static void load() {
instance = new ClientHandler();
@@ -305,6 +333,15 @@ public class ClientHandler {
if (NEIClientConfig.isEnabled()) WorldOverlayRenderer.render(event.partialTicks);
}
@SideOnly(Side.CLIENT)
@SubscribeEvent
public void onWorldUnload(WorldEvent.Unload event) {
Minecraft mc = Minecraft.getMinecraft();
if (event.world == mc.theWorld) {
NEIClientConfig.unloadWorld();
}
}
public void loadWorld(World world, boolean fromServer) {
if (world != lastworld) {
SMPmagneticItems.clear();
@@ -81,7 +81,6 @@ public class ContainerPotionCreator extends ContainerExtended {
public void markDirty() {
super.markDirty();
NEIClientConfig.global.nbt.setTag("potionStore", tag);
NEIClientConfig.global.saveNBT();
}
}
@@ -62,7 +62,6 @@ public class GuiEnchantmentModifier extends GuiContainer {
public static void toggleEnchantmentValidation() {
NEIClientConfig.world.nbt.setBoolean("validateenchantments", !validateEnchantments());
NEIClientConfig.world.saveNBT();
}
protected void actionPerformed(GuiButton guibutton) {
+7 -2
View File
@@ -1,6 +1,7 @@
package codechicken.nei;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashSet;
import java.util.LinkedList;
import java.util.List;
@@ -84,6 +85,10 @@ public class ItemList {
this.filters = filters;
}
public AllMultiItemFilter(ItemFilter... filters) {
this(Arrays.asList(filters));
}
public AllMultiItemFilter() {
this(new LinkedList<>());
}
@@ -91,7 +96,7 @@ public class ItemList {
@Override
public boolean matches(ItemStack item) {
for (ItemFilter filter : filters) try {
if (!filter.matches(item)) return false;
if (filter != null && !filter.matches(item)) return false;
} catch (Exception e) {
NEIClientConfig.logger.error("Exception filtering " + item + " with " + filter, e);
}
@@ -254,7 +259,7 @@ public class ItemList {
try {
filtered = ItemList.forkJoinPool.submit(
() -> items.parallelStream().filter(PresetsWidget::matches).filter(filter::matches)
() -> items.parallelStream().filter(filter::matches)
.collect(Collectors.toCollection(ArrayList::new)))
.get();
} catch (InterruptedException | ExecutionException e) {
+4 -4
View File
@@ -64,18 +64,18 @@ public class ItemPanel extends PanelWidget {
if (newItems != null) {
realItems = newItems;
newItems = null;
onGridChanged();
onItemsChanged();
}
super.refresh(gui);
}
@Override
protected void drawSlotOutline(@Nullable ItemPanelSlot focused, int slotIdx, Rectangle4i rect) {
protected void beforeDrawSlot(@Nullable ItemPanelSlot focused, int slotIdx, Rectangle4i rect) {
if (PresetsWidget.inEditMode()) {
if (!PresetsWidget.isHidden(getItem(slotIdx))) drawRect(rect.x, rect.y, rect.w, rect.h, 0xee555555);
} else {
super.drawSlotOutline(focused, slotIdx, rect);
super.beforeDrawSlot(focused, slotIdx, rect);
}
}
@@ -142,7 +142,7 @@ public class ItemPanel extends PanelWidget {
}
public String getLabelText() {
return String.format("(%d/%d)", getPage(), Math.max(1, getNumPages()));
return String.format("%d/%d", getPage(), Math.max(1, getNumPages()));
}
protected String getPositioningSettingName() {
@@ -203,6 +203,11 @@ public class ItemStackMap<T> {
return map == null ? null : map.get(key);
}
public T getOrDefault(ItemStack key, T defaultValue) {
T e = get(key);
return e == null ? defaultValue : e;
}
public void put(ItemStack key, T value) {
if (key == null || key.getItem() == null) return;
@@ -1,5 +1,7 @@
package codechicken.nei;
import java.util.List;
import net.minecraft.block.Block;
import net.minecraft.item.Item;
import net.minecraft.item.ItemStack;
@@ -27,6 +29,11 @@ public class ItemStackSet extends ItemStackMap<ItemStack> implements ItemFilter
put(item, item);
}
public ItemStackSet addAll(List<ItemStack> items) {
for (ItemStack item : items) add(item);
return this;
}
public boolean contains(ItemStack item) {
return get(item) != null;
}
+51 -155
View File
@@ -1,37 +1,27 @@
package codechicken.nei;
import static codechicken.lib.gui.GuiDraw.drawRect;
import static codechicken.lib.gui.GuiDraw.getMousePosition;
import java.awt.Point;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import javax.annotation.Nullable;
import net.minecraft.client.Minecraft;
import net.minecraft.client.gui.GuiButton;
import net.minecraft.client.gui.ScaledResolution;
import net.minecraft.client.gui.inventory.GuiContainer;
import net.minecraft.client.renderer.OpenGlHelper;
import net.minecraft.client.renderer.RenderHelper;
import net.minecraft.client.renderer.Tessellator;
import net.minecraft.client.shader.Framebuffer;
import net.minecraft.inventory.Slot;
import net.minecraft.item.ItemStack;
import org.lwjgl.opengl.GL11;
import org.lwjgl.opengl.GL12;
import codechicken.lib.vec.Rectangle4i;
import codechicken.nei.ItemPanel.ItemPanelSlot;
import codechicken.nei.api.GuiInfo;
import codechicken.nei.guihook.GuiContainerManager;
import codechicken.nei.recipe.BookmarkRecipeId;
import codechicken.nei.recipe.GuiCraftingRecipe;
import codechicken.nei.recipe.GuiRecipe;
import codechicken.nei.recipe.StackInfo;
public class ItemsGrid {
@@ -53,7 +43,7 @@ public class ItemsGrid {
protected int rows;
protected int columns;
protected boolean showRecipeTooltips = false;
protected List<Integer> gridMask = null;
protected boolean[] invalidSlotMap;
@@ -79,7 +69,7 @@ public class ItemsGrid {
public int indexOf(ItemStack stackA, boolean useNBT) {
for (int idx = 0; idx < realItems.size(); idx++) {
if (StackInfo.equalItemAndNBT(stackA, getItem(idx), useNBT)) {
if (StackInfo.equalItemAndNBT(stackA, realItems.get(idx), useNBT)) {
return idx;
}
}
@@ -116,6 +106,10 @@ public class ItemsGrid {
return columns;
}
public int getLastRowIndex() {
return this.columns > 0 ? (int) Math.ceil((float) this.getMask().size() / this.columns) - 1 : 0;
}
public void setGridSize(int mleft, int mtop, int w, int h) {
marginLeft = mleft;
marginTop = mtop;
@@ -159,10 +153,11 @@ public class ItemsGrid {
protected void onGridChanged() {
refreshBuffer = true;
this.gridMask = null;
}
public void setShowRecipeTooltips(boolean show) {
this.showRecipeTooltips = show;
protected void onItemsChanged() {
onGridChanged();
}
public void refresh(GuiContainer gui) {
@@ -198,9 +193,9 @@ public class ItemsGrid {
for (int r = 0; r < rows; r++) {
final int idx = columns * r + c;
if (GuiInfo.hideItemPanelSlot(gui, getSlotRect(r, c)) && idx >= 0
&& idx < invalidSlotMap.length
&& !invalidSlotMap[idx]) {
if (idx >= 0 && idx < invalidSlotMap.length
&& !invalidSlotMap[idx]
&& GuiInfo.hideItemPanelSlot(gui, getSlotRect(r, c))) {
invalidSlotMap[idx] = true;
validColumn = false;
perPage--;
@@ -238,154 +233,51 @@ public class ItemsGrid {
}
protected List<Integer> getMask() {
List<Integer> mask = new ArrayList<>();
int idx = page * perPage;
for (int i = 0; i < rows * columns && idx < size(); i++) {
mask.add(isInvalidSlot(i) ? null : idx++);
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++);
}
}
return mask;
return this.gridMask;
}
private void drawSlotOutlines(int mousex, int mousey) {
ItemPanelSlot focused = getSlotMouseOver(mousex, mousey);
private 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) {
drawSlotOutline(focused, mask.get(i), getSlotRect(i));
beforeDrawSlot(focused, mask.get(i), getSlotRect(i));
}
}
}
private int recipeTooltipSlotIdx = -1;
private int recipeTooltipLines = 2;
private Runnable recipeTooltipUpdater = null;
private GuiRecipe<?> recipeTooltipGui = null;
public void update() {
if (recipeTooltipUpdater != null) {
recipeTooltipUpdater.run();
recipeTooltipUpdater = null;
}
if (recipeTooltipGui != null) {
recipeTooltipGui.updateAsTooltip();
}
}
private static <T> List<T> listOrEmptyList(final List<T> listOrNull) {
return listOrNull == null ? Collections.emptyList() : listOrNull;
}
private void drawRecipeTooltip(int mousex, int mousey, List<String> itemTooltip) {
if (!NEIClientConfig.isLoaded()) {
return;
}
final Minecraft mc = Minecraft.getMinecraft();
ItemPanelSlot focused = getSlotMouseOver(mousex, mousey);
if (focused == null) {
recipeTooltipSlotIdx = -1;
recipeTooltipGui = null;
return;
}
private void afterDrawItems(int mousex, int mousey, @Nullable ItemPanelSlot focused) {
final List<Integer> mask = getMask();
int slotIdx = -1;
for (int i = 0; i < mask.size(); i++) {
if (mask.get(i) == null) {
continue;
if (mask.get(i) != null) {
afterDrawSlot(focused, mask.get(i), getSlotRect(i));
}
if (focused.slotIndex != mask.get(i)) {
continue;
}
slotIdx = i;
break;
}
if (slotIdx == -1) {
recipeTooltipSlotIdx = -1;
recipeTooltipGui = null;
return;
}
final Point mouseover = getMousePosition();
final ItemPanelSlot panelSlot = ItemPanels.bookmarkPanel.getSlotMouseOver(mouseover.x, mouseover.y);
final BookmarkRecipeId recipeId;
if (panelSlot != null) {
recipeId = ItemPanels.bookmarkPanel.getBookmarkRecipeId(panelSlot.slotIndex);
} else {
recipeId = ItemPanels.bookmarkPanel.getBookmarkRecipeId(focused.item);
}
if (recipeId == null) {
return;
}
if (slotIdx != recipeTooltipSlotIdx) {
recipeTooltipSlotIdx = slotIdx;
recipeTooltipGui = null;
recipeTooltipUpdater = () -> {
recipeTooltipGui = GuiCraftingRecipe.createRecipeGui("item", false, false, false, focused.item);
if (recipeTooltipGui != null) {
recipeTooltipGui.limitToOneRecipe();
recipeTooltipGui.initGui(true);
recipeTooltipGui.guiTop = 0;
recipeTooltipGui.guiLeft = 0;
}
recipeTooltipLines = Math.max(1, itemTooltip.size());
};
}
if (recipeTooltipGui == null) {
return;
}
GL11.glPushMatrix();
final float tooltipYOffset;
if (mousey - marginTop > height / 2) {
tooltipYOffset = mousey - recipeTooltipGui.getHeightAsWidget() + 8;
} else {
tooltipYOffset = mousey + ((recipeTooltipLines < 2) ? 1 : 3 + ((recipeTooltipLines - 1) * 10));
}
GL11.glTranslatef(mousex, tooltipYOffset, 500);
final GuiContainer gui;
if (mc.currentScreen instanceof GuiRecipe) {
gui = ((GuiRecipe<?>) mc.currentScreen).firstGui;
} else if (mc.currentScreen instanceof GuiContainer) {
gui = (GuiContainer) mc.currentScreen;
} else {
gui = null;
}
GL11.glPushAttrib(GL11.GL_ALL_ATTRIB_BITS);
RenderHelper.disableStandardItemLighting();
recipeTooltipGui.drawGuiContainerBackgroundLayer(0.0f, -100, -100);
GL11.glPopAttrib();
if (recipeTooltipGui.slotcontainer != null) {
GL11.glPushAttrib(GL11.GL_ALL_ATTRIB_BITS);
RenderHelper.enableGUIStandardItemLighting();
GL11.glEnable(GL12.GL_RESCALE_NORMAL);
@SuppressWarnings("unchecked")
List<Slot> slots = (List<Slot>) recipeTooltipGui.slotcontainer.inventorySlots;
for (Slot slot : slots) {
if (slot != null && slot.getStack() != null) {
GuiContainerManager.drawItem(slot.xDisplayPosition, slot.yDisplayPosition, slot.getStack());
}
}
GL11.glPopAttrib();
}
recipeTooltipGui.drawGuiContainerForegroundLayer(-100, -100);
for (GuiButton btn : recipeTooltipGui.getOverlayButtons()) {
btn.drawButton(mc, -100, -100);
}
GL11.glPopMatrix();
}
protected void drawSlotOutline(@Nullable ItemPanelSlot focused, int slotIdx, Rectangle4i rect) {
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 afterDrawSlot(@Nullable ItemPanelSlot focused, int slotIdx, Rectangle4i rect) {}
private void blitExistingBuffer() {
Minecraft minecraft = Minecraft.getMinecraft();
GL11.glEnable(GL11.GL_BLEND);
@@ -427,12 +319,18 @@ public class ItemsGrid {
GuiContainerManager.disableMatrixStackLogging();
}
protected boolean shouldCacheItemRendering() {
return NEIClientConfig.shouldCacheItemRendering();
}
public void draw(int mousex, int mousey) {
if (getPerPage() == 0) {
return;
}
if (NEIClientConfig.shouldCacheItemRendering()) {
final ItemPanelSlot focused = getSlotMouseOver(mousex, mousey);
if (shouldCacheItemRendering()) {
if (refreshBuffer) {
Minecraft minecraft = Minecraft.getMinecraft();
@@ -457,23 +355,17 @@ public class ItemsGrid {
refreshBuffer = false;
}
drawSlotOutlines(mousex, mousey);
beforeDrawItems(mousex, mousey, focused);
blitExistingBuffer();
} else {
drawSlotOutlines(mousex, mousey);
beforeDrawItems(mousex, mousey, focused);
drawItems();
}
afterDrawItems(mousex, mousey, focused);
}
public void postDrawTooltips(int mousex, int mousey, List<String> tooltip) {
if (NEIClientConfig.showRecipeTooltips() && showRecipeTooltips) {
try {
drawRecipeTooltip(mousex, mousey, tooltip);
} catch (Exception e) {
NEIClientConfig.logger.warn("Cannot draw recipe tooltip", e);
}
}
}
public void postDrawTooltips(int mousex, int mousey, List<String> tooltip) {}
public void setVisible() {
if (getItems().isEmpty() && getMessageOnEmpty() != null) {
@@ -510,16 +402,20 @@ public class ItemsGrid {
}
public boolean contains(int px, int py) {
final Rectangle4i rect = new Rectangle4i(
marginLeft + paddingLeft,
marginTop,
columns * SLOT_SIZE,
rows * SLOT_SIZE);
if (!(new Rectangle4i(marginLeft + paddingLeft, marginTop, columns * SLOT_SIZE, height)).contains(px, py)) {
if (!rect.contains(px, py)) {
return false;
}
final int r = (int) ((py - marginTop) / SLOT_SIZE);
final int c = (int) ((px - marginLeft - paddingLeft) / SLOT_SIZE);
final int slt = columns * r + c;
return r >= rows || c >= columns || !isInvalidSlot(slt);
return !isInvalidSlot(columns * r + c);
}
public String getMessageOnEmpty() {
@@ -14,7 +14,6 @@ import static codechicken.nei.NEIClientConfig.isEnabled;
import static codechicken.nei.NEIClientConfig.isHidden;
import static codechicken.nei.NEIClientConfig.showIDs;
import static codechicken.nei.NEIClientConfig.toggleBooleanSetting;
import static codechicken.nei.NEIClientConfig.world;
import static codechicken.nei.NEIClientUtils.cycleGamemode;
import static codechicken.nei.NEIClientUtils.decreaseSlotStack;
import static codechicken.nei.NEIClientUtils.deleteEverything;
@@ -226,10 +225,6 @@ public class LayoutManager implements IContainerInputHandler, IContainerTooltipH
return true;
}
if (NEIClientConfig.isKeyHashDown("gui.bookmark_pull_items")) {
return bookmarkPanel.pullBookmarkItems();
}
if (isEnabled() && !isHidden()) {
for (Widget widget : controlWidgets) if (inputFocused == null) widget.lastKeyTyped(keyID, keyChar);
}
@@ -286,7 +281,7 @@ public class LayoutManager implements IContainerInputHandler, IContainerTooltipH
@Override
public void postRenderTooltips(GuiContainer gui, int mousex, int mousey, List<String> tooltip) {
if (!isHidden() && isEnabled()) {
if (!isHidden() && isEnabled() && GuiContainerManager.shouldShowTooltip(gui)) {
for (Widget widget : drawWidgets) widget.postDrawTooltips(mousex, mousey, tooltip);
}
}
@@ -337,6 +332,8 @@ public class LayoutManager implements IContainerInputHandler, IContainerTooltipH
if (gui.guiLeft - 4 < 76) visiblity.showWidgets = false;
if (!itemsLoaded) visiblity.showPresetsDropdown = false;
try {
GuiInfo.readLock.lock();
GuiInfo.guiHandlers.forEach(handler -> handler.modifyVisiblity(gui, visiblity));
@@ -735,7 +732,7 @@ public class LayoutManager implements IContainerInputHandler, IContainerTooltipH
@Override
public boolean shouldShowTooltip(GuiContainer gui) {
return itemPanel.draggedStack == null && bookmarkPanel.sortedStackIndex == -1;
return itemPanel.draggedStack == null && !bookmarkPanel.inEditingState();
}
/** Note: this method isn't actually used by this mod, but NEI add-ons might need it. */
@@ -758,17 +755,21 @@ public class LayoutManager implements IContainerInputHandler, IContainerTooltipH
@Override
public void renderSlotOverlay(GuiContainer window, Slot slot) {
ItemStack item = slot.getStack();
if (world.nbt.getBoolean("searchinventories") && (item == null ? !getSearchExpression().equals("")
: !((SearchField) 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);
if (SearchField.searchInventories()) {
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);
}
}
}
public static void drawIcon(int x, int y, Image image) {
@@ -73,6 +73,7 @@ public class NEIClientConfig {
public static final File serialHandlersFile = new File(configDir, "serialhandlers.cfg");
public static final File heightHackHandlersFile = new File(configDir, "heighthackhandlers.cfg");
public static final File handlerOrderingFile = new File(configDir, "handlerordering.csv");
public static final File hiddenHandlersFile = new File(configDir, "hiddenhandlers.csv");
@Deprecated
public static File bookmarkFile;
@@ -84,6 +85,9 @@ public class NEIClientConfig {
// We use regex here so that we can apply the height hack to entire mods with one entry.
public static HashSet<Pattern> heightHackHandlerRegex = new HashSet<>();
// Set of handler Name or Id of handlers that need hide.
public static HashSet<String> hiddenHandlers = new HashSet<>();
// Map of handler ID to sort order.
// Handlers will be sorted in ascending order, so smaller numbers show up earlier.
// Any handler not in the map will be assigned to 0, and negative numbers are fine.
@@ -237,14 +241,14 @@ public class NEIClientConfig {
tag.getTag("inventory.bookmarksEnabled").setComment("Enable/disable bookmarks").getBooleanValue(true);
API.addOption(new OptionToggleButton("inventory.bookmarksEnabled", true));
tag.getTag("inventory.useNBTInBookmarks").setComment("Use NBT in Bookmarks").getBooleanValue(true);
API.addOption(new OptionToggleButton("inventory.useNBTInBookmarks", true));
tag.getTag("inventory.bookmarksAnimationEnabled").setComment("REI Style Animation in Bookmarks")
.getBooleanValue(true);
API.addOption(new OptionToggleButton("inventory.bookmarksAnimationEnabled", true));
tag.getTag("inventory.recipeTooltipsEnabled").setComment("Show recipe tooltips in Bookmarks")
.getBooleanValue(true);
API.addOption(new OptionToggleButton("inventory.recipeTooltipsEnabled", true));
tag.getTag("inventory.recipeTooltipsMode").setComment("Show recipe tooltips in Bookmarks").getIntValue(1);
API.addOption(new OptionCycled("inventory.recipeTooltipsMode", 4, true));
tag.getTag("inventory.showRecipeMarker").setComment("Show Recipe Marker").getBooleanValue(false);
API.addOption(new OptionToggleButton("inventory.showRecipeMarker", true));
@@ -256,9 +260,11 @@ public class NEIClientConfig {
tag.getTag("inventory.jei_style_tabs").setComment("Enable/disable JEI Style Tabs").getBooleanValue(true);
API.addOption(new OptionToggleButtonBoubs("inventory.jei_style_tabs", true));
tag.getTag("inventory.jei_style_item_presence_overlay")
.setComment("Enable/disable JEI Style item presence overlay on ?-hover").getBooleanValue(true);
API.addOption(new OptionToggleButton("inventory.jei_style_item_presence_overlay", true));
tag.getTag("inventory.jei_style_recipe_catalyst").setComment("Enable/disable JEI Style Recipe Catalysts")
.getBooleanValue(true);
API.addOption(new OptionToggleButton("inventory.jei_style_recipe_catalyst", true));
@@ -355,6 +361,7 @@ public class NEIClientConfig {
"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);
@@ -424,6 +431,16 @@ public class NEIClientConfig {
world.saveNBT();
}
public static void unloadWorld() {
if (ItemPanels.bookmarkPanel != null) {
ItemPanels.bookmarkPanel.saveBookmarks();
}
if (world != null) {
world.saveNBT();
}
}
public static int getKeyBinding(String string) {
return getSetting("keys." + string).getIntValue(Keyboard.KEY_NONE);
}
@@ -556,16 +573,12 @@ public class NEIClientConfig {
return !getBooleanSetting("inventory.bookmarksEnabled");
}
public static boolean useNBTInBookmarks() {
return getBooleanSetting("inventory.useNBTInBookmarks");
}
public static boolean areBookmarksAnimated() {
return getBooleanSetting("inventory.bookmarksAnimationEnabled");
}
public static boolean showRecipeTooltips() {
return getBooleanSetting("inventory.recipeTooltipsEnabled");
public static int getRecipeTooltipsMode() {
return getIntSetting("inventory.recipeTooltipsMode");
}
public static boolean showRecipeMarker() {
@@ -689,7 +702,6 @@ public class NEIClientConfig {
public static void setSearchExpression(String expression) {
world.nbt.setString("search", expression);
world.saveNBT();
}
public static boolean isMouseScrollTransferEnabled() {
@@ -394,7 +394,6 @@ public class NEIClientUtils extends NEIServerUtils {
public static void setItemQuantity(int i) {
world.nbt.setInteger("quantity", i);
world.saveNBT();
}
public static GuiContainer getGuiContainer() {
@@ -328,7 +328,7 @@ public abstract class PanelWidget extends Widget {
ItemPanelSlot hoverSlot = getSlotMouseOver(mousex, mousey);
if (hoverSlot != null && hoverSlot.slotIndex == mouseDownSlot && draggedStack == null) {
ItemStack item = hoverSlot.item;
ItemStack item = hoverSlot.item.copy();
if (NEIController.manager.window instanceof GuiRecipe || !NEIClientConfig.canCheatItem(item)) {
@@ -26,8 +26,11 @@ public class PositionedStack {
relx = x;
rely = y;
if (genPerms) generatePermutations();
else setPermutationToRender(0);
if (genPerms) {
generatePermutations();
} else {
setPermutationToRender(0);
}
}
public PositionedStack(Object object, int x, int y) {
@@ -77,10 +80,13 @@ public class PositionedStack {
public void setPermutationToRender(int index) {
item = items[index].copy();
if (item.getItem() == null) item = new ItemStack(Blocks.fire);
else if (item.getItemDamage() == OreDictionary.WILDCARD_VALUE && item.getItem() != null
&& item.getItem().isRepairable())
item.setItemDamage(0);
if (item.getItem() == null) {
item = new ItemStack(Blocks.fire);
} else if (item.getItemDamage() == OreDictionary.WILDCARD_VALUE && item.getItem() != null
&& item.getItem().isRepairable()) {
item.setItemDamage(0);
}
}
public boolean contains(ItemStack ingredient) {
+208 -57
View File
@@ -6,6 +6,8 @@ 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.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
@@ -30,39 +32,55 @@ import codechicken.core.CommonUtils;
import codechicken.core.gui.GuiScrollSlot;
import codechicken.lib.gui.GuiDraw;
import codechicken.lib.vec.Rectangle4i;
import codechicken.nei.ItemList.ItemsLoadedCallback;
import codechicken.nei.ItemPanel.ItemPanelSlot;
import codechicken.nei.api.API;
import codechicken.nei.api.IRecipeFilter;
import codechicken.nei.api.IRecipeFilter.IRecipeFilterProvider;
import codechicken.nei.api.ItemFilter;
import codechicken.nei.api.ItemFilter.ItemFilterProvider;
import codechicken.nei.recipe.GuiRecipe;
import codechicken.nei.recipe.IRecipeHandler;
import codechicken.nei.recipe.StackInfo;
import codechicken.nei.util.NBTJson;
import cpw.mods.fml.common.registry.GameRegistry;
public class PresetsWidget extends Widget {
public class PresetsWidget extends Widget implements ItemFilterProvider, ItemsLoadedCallback, ItemFilter {
public static class PresetTag implements ItemFilter {
protected static enum PresetTagState {
WHITELIST,
BLACKLIST,
BLACKLIST_PLUS;
public PresetTagState next() {
if (this == PresetTagState.WHITELIST) return PresetTagState.BLACKLIST;
if (this == PresetTagState.BLACKLIST) return PresetTagState.BLACKLIST_PLUS;
if (this == PresetTagState.BLACKLIST_PLUS) return PresetTagState.WHITELIST;
return this;
}
}
public static class PresetTag {
public String filename = null;
public String displayName = "";
public boolean whitelist = true;
public HashSet<String> items;
public PresetTagState state = PresetTagState.BLACKLIST;
public ItemStackSet items;
public PresetTag(String displayName) {
this(displayName, new HashSet<>(), false, null);
this(displayName, null, PresetTagState.BLACKLIST, null);
}
public PresetTag(String displayName, HashSet<String> items) {
this(displayName, items, true, null);
}
public PresetTag(String displayName, HashSet<String> items, boolean whitelist, String filename) {
public PresetTag(String displayName, ItemStackSet items, PresetTagState state, String filename) {
this.displayName = displayName;
this.items = items != null ? items : new HashSet<>();
this.whitelist = whitelist;
this.items = items != null ? items : new ItemStackSet();
this.state = state;
this.filename = filename;
}
public boolean matches(final ItemStack stack) {
return items.contains(StackInfo.getItemStackGUID(stack)) == whitelist;
return items.contains(stack) == (state == PresetTagState.WHITELIST);
}
public static PresetTag loadFromFile(File file) {
@@ -84,14 +102,18 @@ public class PresetsWidget extends Widget {
final JsonParser parser = new JsonParser();
final JsonObject metaObject = parser.parse(itemStrings.remove(0)).getAsJsonObject();
final String displayName = metaObject.get("displayName").getAsString();
final boolean whitelist = metaObject.get("whitelist").getAsBoolean();
final HashSet<String> items = new HashSet<>();
final PresetTagState state = metaObject.has("state")
? PresetTagState.valueOf(metaObject.get("state").getAsString())
: PresetTagState.BLACKLIST;
final ItemStackSet items = new ItemStackSet();
for (String itemStr : itemStrings) {
items.add(itemStr);
for (ItemStack stack : ItemList.items) {
if (itemStrings.contains(StackInfo.getItemStackGUID(stack))) {
items.add(stack);
}
}
return new PresetTag(displayName, items, whitelist, file.getName());
return new PresetTag(displayName, items, state, file.getName());
} catch (Throwable th) {
NEIClientConfig.logger.error("Failed to load presets ItemStack from file", file);
}
@@ -127,12 +149,16 @@ public class PresetsWidget extends Widget {
final JsonObject row = new JsonObject();
row.add("displayName", new JsonPrimitive(displayName));
row.add("whitelist", new JsonPrimitive(whitelist));
row.add("state", new JsonPrimitive(state.toString()));
strings.add(NBTJson.toJson(row));
try {
strings.addAll(items);
for (ItemStack stack : items.values()) {
strings.add(StackInfo.getItemStackGUID(stack));
}
} catch (JsonSyntaxException e) {
NEIClientConfig.logger.error("Failed to stringify presets ItemStack to json string");
}
@@ -146,9 +172,8 @@ public class PresetsWidget extends Widget {
}
}
@SuppressWarnings("unchecked")
public PresetTag copy() {
return new PresetTag(displayName, (HashSet) items.clone(), whitelist, null);
return new PresetTag(displayName, new ItemStackSet().addAll(items.values()), state, null);
}
}
@@ -290,7 +315,7 @@ public class PresetsWidget extends Widget {
selected.remove(tag);
tag.deleteFile();
} else if (direction.contains(mx, my)) {
tag.whitelist = !tag.whitelist;
tag.state = tag.state.next();
tag.saveToFile();
}
@@ -299,6 +324,7 @@ public class PresetsWidget extends Widget {
PresetsWidget.openListBox = !presets.isEmpty()
&& (NEIClientUtils.controlKey() || NEIClientUtils.shiftKey() || !option.contains(mx, my));
ItemList.updateFilter.restart();
PresetsRecipeFilter.blacklist = null;
}
@Override
@@ -319,13 +345,22 @@ public class PresetsWidget extends Widget {
final String displayName = NEIClientUtils
.cropText(PresetsWidget.fontRenderer, tag.displayName, option.w - 6);
final String dirName = tag.whitelist ? translate("presets.whitelist.label")
: translate("presets.blacklist.label");
String stateLabel = translate("presets.blacklist.label");
if (tag.state == PresetTagState.WHITELIST) {
stateLabel = translate("presets.whitelist.label");
} else if (tag.state == PresetTagState.BLACKLIST_PLUS) {
stateLabel = translate("presets.blacklistPlus.label");
}
// blacklist or whitelist
LayoutManager.getLayoutStyle()
.drawSubsetTag(null, direction.x, direction.y, direction.w, direction.h, directionState, false);
GuiDraw.drawString(dirName, direction.x + 6, direction.y + 5, directionState == 2 ? 0xFFE0E0E0 : 0xFFFFA0);
GuiDraw.drawString(
stateLabel,
direction.x + 6,
direction.y + 5,
directionState == 2 ? 0xFFE0E0E0 : 0xFFFFA0);
// option name
LayoutManager.getLayoutStyle()
@@ -433,6 +468,70 @@ public class PresetsWidget extends Widget {
}
}
protected static class PresetsRecipeFilter implements IRecipeFilterProvider, IRecipeFilter {
protected static ItemStackSet blacklist;
public IRecipeFilter getFilter() {
if (PresetsWidget.edit != null) {
return null;
}
if (blacklist == null) {
blacklist = new ItemStackSet();
for (PresetTag tag : listbox.getSelected()) {
if (tag.state == PresetTagState.BLACKLIST_PLUS) {
blacklist.addAll(tag.items.values());
}
}
}
return blacklist.isEmpty() ? null : this;
}
@Override
public boolean matches(IRecipeHandler handler, List<PositionedStack> ingredients, PositionedStack result,
List<PositionedStack> others) {
if (matchPositionedStack(ingredients, false)) {
return false;
}
if (result != null && matchPositionedStack(result)) {
return true;
}
if (!others.isEmpty() && matchPositionedStack(others, true)) {
return true;
}
return result == null && others.isEmpty();
}
private boolean matchPositionedStack(List<PositionedStack> items, boolean dir) {
for (PositionedStack pStack : items) {
if (matchPositionedStack(pStack) == dir) {
return true;
}
}
return false;
}
private boolean matchPositionedStack(PositionedStack pStack) {
for (ItemStack stack : pStack.items) {
if (!blacklist.contains(stack)) {
return true;
}
}
return false;
}
}
protected static final FontRenderer fontRenderer = Minecraft.getMinecraft().fontRenderer;
protected static SubsetListBox listbox = new SubsetListBox();
protected static File presetsDir;
@@ -517,7 +616,7 @@ public class PresetsWidget extends Widget {
public boolean onButtonPress(boolean rightclick) {
if (!rightclick && edit != null) {
edit.whitelist = !edit.whitelist;
edit.state = edit.state.next();
return true;
}
@@ -526,20 +625,54 @@ public class PresetsWidget extends Widget {
@Override
public String getRenderLabel() {
return edit != null && edit.whitelist ? translate("presets.whitelist.label")
: translate("presets.blacklist.label");
if (edit == null || edit.state == PresetTagState.BLACKLIST) {
return translate("presets.blacklist.label");
} else if (edit.state == PresetTagState.WHITELIST) {
return translate("presets.whitelist.label");
} else if (edit.state == PresetTagState.BLACKLIST_PLUS) {
return translate("presets.blacklistPlus.label");
}
return translate("presets.blacklist.label");
}
@Override
public void addTooltips(List<String> tooltip) {
if (edit != null) {
tooltip.add(
edit.whitelist ? translate("presets.whitelist.tooltip")
: translate("presets.blacklist.tooltip"));
if (edit.state == PresetTagState.BLACKLIST) {
tooltip.add(translate("presets.blacklist.tooltip"));
} else if (edit.state == PresetTagState.WHITELIST) {
tooltip.add(translate("presets.whitelist.tooltip"));
} else if (edit.state == PresetTagState.BLACKLIST_PLUS) {
tooltip.add(translate("presets.blacklistPlus.tooltip"));
}
}
}
};
public void itemsLoaded() {
if (presetsDir != null) {
listbox = new SubsetListBox(presetsDir);
edit = null;
openListBox = false;
selectedDisplayName.setText("");
ItemList.updateFilter.restart();
PresetsRecipeFilter.blacklist = null;
}
}
public PresetsWidget() {
API.addItemFilter(this);
API.addRecipeFilter(new PresetsRecipeFilter());
ItemList.loadCallbacks.add(this);
}
protected static void setEditedTag(final PresetTag tag) {
edit = tag;
openListBox = false;
@@ -564,19 +697,15 @@ public class PresetsWidget extends Widget {
if (edit != null) {
if (mouseSelection != null && mouseSelection.items.contains(item)) {
return mouseSelection.append != edit.whitelist;
return mouseSelection.append != (edit.state == PresetTagState.WHITELIST);
}
return !edit.matches(item);
}
if (!listbox.getSelected().isEmpty()) {
final List<PresetTag> selected = listbox.getSelected();
for (int idx = 0; idx < selected.size(); idx++) {
if (!selected.get(idx).matches(item)) {
return true;
}
for (PresetTag tag : listbox.getSelected()) {
if (!tag.matches(item)) {
return true;
}
}
@@ -619,12 +748,10 @@ public class PresetsWidget extends Widget {
}
protected static void hideItem(final ItemStack stack, boolean append) {
final String guid = StackInfo.getItemStackGUID(stack);
if (append) {
edit.items.add(guid);
edit.items.add(stack);
} else {
edit.items.remove(guid);
edit.items.remove(stack);
}
}
@@ -637,24 +764,44 @@ public class PresetsWidget extends Widget {
}
public static void loadPresets(final String worldPath) {
final File dir = new File(CommonUtils.getMinecraftDir(), "saves/NEI/" + worldPath);
presetsDir = new File(CommonUtils.getMinecraftDir(), "saves/NEI/" + worldPath + "/presets");
if (!dir.exists()) {
dir.mkdirs();
if (!presetsDir.getParentFile().exists()) {
presetsDir.getParentFile().mkdirs();
}
if (!presetsDir.exists()) {
presetsDir.mkdirs();
}
listbox = new SubsetListBox(presetsDir);
if (!(new File(presetsDir, "selected.ini")).exists()) {
final File configPresets = new File(NEIClientConfig.configDir, "/presets");
edit = null;
openListBox = false;
selectedDisplayName.setText("");
if (configPresets.exists()) {
for (File file : configPresets.listFiles()) {
try {
InputStream src = new FileInputStream(file);
OutputStream dst = new FileOutputStream(new File(presetsDir, file.getName()));
IOUtils.copy(src, dst);
src.close();
dst.close();
} catch (IOException e) {}
}
}
}
if (LayoutManager.itemsLoaded) {
listbox = new SubsetListBox(presetsDir);
edit = null;
openListBox = false;
selectedDisplayName.setText("");
ItemList.updateFilter.restart();
PresetsRecipeFilter.blacklist = null;
}
ItemList.updateFilter.restart();
}
@Override
@@ -903,16 +1050,20 @@ public class PresetsWidget extends Widget {
return tooltip;
}
public static boolean matches(ItemStack stack) {
@Override
public ItemFilter getFilter() {
return this;
}
@Override
public boolean matches(ItemStack stack) {
if (edit != null) {
return true;
}
final List<PresetTag> selected = listbox.getSelected();
for (int idx = 0; idx < selected.size(); idx++) {
if (!selected.get(idx).matches(stack)) {
for (PresetTag tag : listbox.getSelected()) {
if (!tag.matches(stack)) {
return false;
}
}
@@ -0,0 +1,78 @@
package codechicken.nei;
import net.minecraft.util.EnumChatFormatting;
import codechicken.nei.api.ItemFilter;
import codechicken.nei.util.TextHistory;
public abstract class RecipeSearchField extends TextField {
private static final TextHistory history = new TextHistory();
public RecipeSearchField(String ident) {
super(ident);
this.field.setVisible(false);
}
public boolean isVisible() {
return this.field.getVisible();
}
public void setVisible(boolean visible) {
this.field.setVisible(visible);
}
protected abstract boolean noResults();
@Override
public int getTextColour() {
if (!text().isEmpty() && !noResults()) {
return focused() ? 0xFFcc3300 : 0xFF993300;
} else {
return focused() ? 0xFFE0E0E0 : 0xFF909090;
}
}
@Override
public void lastKeyTyped(int keyID, char keyChar) {
if (focused() && NEIClientConfig.isKeyHashDown("gui.getprevioussearch")) {
handleNavigateHistory(TextHistory.Direction.PREVIOUS);
}
if (focused() && NEIClientConfig.isKeyHashDown("gui.getnextsearch")) {
handleNavigateHistory(TextHistory.Direction.NEXT);
}
}
@Override
public String filterText(String s) {
return EnumChatFormatting.getTextWithoutFormattingCodes(s);
}
public ItemFilter getFilter() {
return SearchField.getFilter(text());
}
@Override
public void setFocus(boolean focus) {
final boolean previousFocus = field.isFocused();
if (previousFocus != focus) {
history.add(text());
}
super.setFocus(focus);
}
private boolean handleNavigateHistory(TextHistory.Direction direction) {
if (focused()) {
return history.get(direction, text()).map(newText -> {
setText(newText);
return true;
}).orElse(false);
}
return false;
}
}
+16 -6
View File
@@ -101,7 +101,6 @@ public class SearchField extends TextField implements ItemFilterProvider {
if (button == 0) {
if (focused() && (System.currentTimeMillis() - lastclicktime < 400)) { // double click
NEIClientConfig.world.nbt.setBoolean("searchinventories", !searchInventories());
NEIClientConfig.world.saveNBT();
}
lastclicktime = System.currentTimeMillis();
}
@@ -123,11 +122,11 @@ public class SearchField extends TextField implements ItemFilterProvider {
if (isVisible() && NEIClientConfig.isKeyHashDown("gui.search")) {
setFocus(true);
}
if (NEIClientConfig.isKeyHashDown("gui.getprevioussearch") && focused()) {
if (focused() && NEIClientConfig.isKeyHashDown("gui.getprevioussearch")) {
handleNavigateHistory(TextHistory.Direction.PREVIOUS);
}
if (NEIClientConfig.isKeyHashDown("gui.getnextsearch") && focused()) {
if (focused() && NEIClientConfig.isKeyHashDown("gui.getnextsearch")) {
handleNavigateHistory(TextHistory.Direction.NEXT);
}
}
@@ -151,22 +150,33 @@ public class SearchField extends TextField implements ItemFilterProvider {
try {
pattern = Pattern.compile(search);
} catch (PatternSyntaxException ignored) {}
return pattern == null || pattern.toString().length() == 0 ? null : pattern;
}
@Override
public ItemFilter getFilter() {
String s_filter = text().toLowerCase();
return getFilter(text());
}
public static ItemFilter getFilter(String s_filter) {
List<ItemFilter> primary = new LinkedList<>();
List<ItemFilter> secondary = new LinkedList<>();
s_filter = s_filter.toLowerCase();
for (ISearchProvider p : searchProviders) {
ItemFilter filter = p.getFilter(s_filter);
if (filter != null) (p.isPrimary() ? primary : secondary).add(filter);
}
if (!primary.isEmpty()) return new AnyMultiItemFilter(primary);
if (!secondary.isEmpty()) return new AnyMultiItemFilter(secondary);
if (!primary.isEmpty()) {
return new AnyMultiItemFilter(primary);
}
if (!secondary.isEmpty()) {
return new AnyMultiItemFilter(secondary);
}
return new EverythingItemFilter();
}
@@ -533,7 +533,6 @@ public class SubsetWidget extends Button implements ItemFilterProvider, ItemsLoa
NBTTagList list = dirtyHiddenItems.getAndSet(null);
if (list != null) {
NEIClientConfig.world.nbt.setTag("hiddenItems", list);
NEIClientConfig.world.saveNBT();
}
}
+24 -16
View File
@@ -27,11 +27,13 @@ import codechicken.nei.SearchField;
import codechicken.nei.SearchField.ISearchProvider;
import codechicken.nei.SubsetWidget;
import codechicken.nei.SubsetWidget.SubsetTag;
import codechicken.nei.api.IRecipeFilter.IRecipeFilterProvider;
import codechicken.nei.api.ItemFilter.ItemFilterProvider;
import codechicken.nei.config.Option;
import codechicken.nei.config.OptionKeyBind;
import codechicken.nei.recipe.CatalystInfo;
import codechicken.nei.recipe.GuiCraftingRecipe;
import codechicken.nei.recipe.GuiRecipe;
import codechicken.nei.recipe.GuiUsageRecipe;
import codechicken.nei.recipe.ICraftingHandler;
import codechicken.nei.recipe.IRecipeHandler;
@@ -160,7 +162,7 @@ public class API {
/**
* Adds an item to the item panel. Any items added using this function will override the default search pattern.
*
*
* @param item an item with data
*/
public static void addItemListEntry(ItemStack item) {
@@ -183,7 +185,7 @@ public class API {
/**
* Add a custom KeyBinding to be configured in the Controls menu.
*
*
* @param ident An identifier for your key, eg "shoot"
* @param defaultKey The default value, commonly obtained from {@link Keyboard}
*/
@@ -195,7 +197,7 @@ public class API {
/**
* Add a custom KeyBinding to be configured in the Controls menu.
*
*
* @param ident An identifier for your key, eg "shoot"
* @param defaultKey The default value, commonly obtained from {@link Keyboard}
*/
@@ -210,7 +212,7 @@ public class API {
/**
* Add a new Layout Style for the NEI interface
*
*
* @param styleID The Unique ID to be used for storing your style in the config and cycling through avaliable styles
* @param style The style to add.
*/
@@ -220,7 +222,7 @@ public class API {
/**
* Registers a new Infinite Item Handler.
*
*
* @param handler The handler to be registered.
*/
public static void addInfiniteItemHandler(IInfiniteItemHandler handler) {
@@ -229,7 +231,7 @@ public class API {
/**
* Registers a new Infinite Item Handler.
*
*
* @param block The block to handle, null for all.
* @param handler The handler to be registered.
*/
@@ -239,7 +241,7 @@ public class API {
/**
* Tells NEI not to perform any Fast Transfer operations on slots of a particular class
*
*
* @param slotClass The class of slot to be exempted
*/
public static void addFastTransferExemptSlot(Class<? extends Slot> slotClass) {
@@ -248,7 +250,7 @@ public class API {
/**
* Register a new text handler for the block highlight tooltip with a layout specification (HEADER, BODY or FOOTER).
*
*
* @param handler The handler to be registered.
* @param layout A HUDAugmenterRegistry.Layout entry. HEADER is displayed before BODY which is displayed before
* FOOTER.
@@ -259,16 +261,22 @@ public class API {
/**
* Register a mode handler for overriding NEI recipe/utility/cheat mode settings.
*
*
* @param handler The handler to be registered.
*/
public static void registerModeHandler(INEIModeHandler handler) {
NEIInfo.modeHandlers.add(handler);
}
public static void addRecipeFilter(IRecipeFilterProvider filterProvider) {
synchronized (GuiRecipe.recipeFilterers) {
GuiRecipe.recipeFilterers.add(filterProvider);
}
}
/**
* Register a filter provider for the item panel.
*
*
* @param filterProvider The filter provider to be registered.
*/
public static void addItemFilter(ItemFilterProvider filterProvider) {
@@ -279,7 +287,7 @@ public class API {
/**
* Adds a new tag to the item subset dropdown.
*
*
* @param name The fully qualified name, Eg Blocks.MobSpawners. NOT case sensitive
* @param filter A filter for matching items that fit in this subset
*/
@@ -289,7 +297,7 @@ public class API {
/**
* Adds a new tag to the item subset dropdown.
*
*
* @param name The fully qualified name, Eg Blocks.MobSpawners. NOT case sensitive
* @param items An iterable of itemstacks to be added as a subset
*/
@@ -315,7 +323,7 @@ public class API {
/**
* Adds a new sorting option to the item panel sort menu
*
*
* @param name A unique id for this sort option. Will be used in the config for saving and translated in the options
* gui. Note that if the translation key name.tip exists, it will be used for a tooltip
*/
@@ -326,7 +334,7 @@ public class API {
/**
* Adds an additional item list entry for an item, sorted after the rest of the items are found through the normal
* process
*
*
* @param item The item to add the variant for
* @param variant The stack to appear in the item panel
*/
@@ -345,7 +353,7 @@ public class API {
* Adds an association between an ingredient and what it can craft. (i.e. Furnace ItemStack -> Smelting and Fuel
* Recipes) Allows players to see what ingredient they need to craft in order to make recipes from a recipe
* category.
*
*
* @param stack the ingredient that can craft recipes (like a furnace or crafting table)
* @param handler the recipe category handled by the ingredient
* @param priority higher priority comes first, default to 0
@@ -366,7 +374,7 @@ public class API {
* Adds an association between an ingredient and what it can craft. (i.e. Furnace ItemStack -> Smelting and Fuel
* Recipes) Allows players to see what ingredient they need to craft in order to make recipes from a recipe
* category.
*
*
* @param stack the ingredient that can craft recipes (like a furnace or crafting table)
* @param handlerID recipe category identifier (see also {@link RecipeCatalysts#getRecipeID(IRecipeHandler)})
* @param priority higher priority comes first, default to 0
@@ -0,0 +1,18 @@
package codechicken.nei.api;
import java.util.List;
import codechicken.nei.PositionedStack;
import codechicken.nei.recipe.IRecipeHandler;
public interface IRecipeFilter {
public static interface IRecipeFilterProvider {
public IRecipeFilter getFilter();
}
public boolean matches(IRecipeHandler handler, List<PositionedStack> ingredients, PositionedStack result,
List<PositionedStack> others);
}
@@ -1,5 +1,8 @@
package codechicken.nei.api;
import static codechicken.lib.gui.GuiDraw.getMousePosition;
import java.awt.Point;
import java.util.List;
import net.minecraft.client.gui.inventory.GuiContainer;
@@ -7,14 +10,18 @@ import net.minecraft.item.ItemStack;
import org.lwjgl.input.Mouse;
import codechicken.nei.ItemPanel.ItemPanelSlot;
import codechicken.nei.ItemPanels;
import codechicken.nei.LayoutManager;
import codechicken.nei.NEIClientConfig;
import codechicken.nei.NEIClientUtils;
import codechicken.nei.PositionedStack;
import codechicken.nei.recipe.BookmarkRecipeId;
import codechicken.nei.recipe.GuiCraftingRecipe;
import codechicken.nei.recipe.GuiRecipe;
import codechicken.nei.recipe.GuiUsageRecipe;
import codechicken.nei.recipe.StackInfo;
import codechicken.nei.recipe.stackinfo.GTFluidStackStringifyHandler;
public abstract class ShortcutInputHandler {
@@ -28,6 +35,23 @@ public abstract class ShortcutInputHandler {
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.bookmark_recipe")) {
final int groupId = ItemPanels.bookmarkPanel.getHoveredGroupId(true);
if (groupId != -1) {
ItemPanels.bookmarkPanel.removeGroup(groupId);
return true;
}
}
if (stackover == null) {
return false;
}
@@ -43,11 +67,11 @@ public abstract class ShortcutInputHandler {
}
if (NEIClientConfig.isKeyHashDown("gui.recipe")) {
return GuiCraftingRecipe.openRecipeGui("item", stackover);
return GuiCraftingRecipe.openRecipeGui("item", normalizeItemStack(stackover));
}
if (NEIClientConfig.isKeyHashDown("gui.usage")) {
return GuiUsageRecipe.openRecipeGui("item", stackover);
return GuiUsageRecipe.openRecipeGui("item", normalizeItemStack(stackover));
}
if (NEIClientConfig.isKeyHashDown("gui.bookmark")) {
@@ -99,14 +123,28 @@ public abstract class ShortcutInputHandler {
return false;
}
private static boolean openOverlayRecipe(ItemStack stack, boolean shift) {
private static boolean openOverlayRecipe(ItemStack stackover, boolean shift) {
final GuiContainer gui = NEIClientUtils.getGuiContainer();
if (gui == null || gui instanceof GuiRecipe) {
return false;
}
return GuiCraftingRecipe.openRecipeGui("item", true, shift, stack);
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);
}
if (recipeId != null) {
return GuiCraftingRecipe.overlayRecipe(stackover, recipeId, shift);
}
return false;
}
private static boolean saveRecipeInBookmark(ItemStack stack, boolean saveIngredients, boolean saveStackSize) {
@@ -128,4 +166,11 @@ public abstract class ShortcutInputHandler {
return false;
}
private static ItemStack normalizeItemStack(ItemStack stack) {
GTFluidStackStringifyHandler.replaceAE2FCFluidDrop = true;
stack = StackInfo.loadFromNBT(StackInfo.itemStackToNBT(stack));
GTFluidStackStringifyHandler.replaceAE2FCFluidDrop = false;
return stack;
}
}
@@ -16,10 +16,6 @@ import codechicken.nei.util.NBTJson;
public class BookmarkRecipeId {
public int position = -1;
public int recipetype = -1;
public String handlerName = null;
public List<NBTTagCompound> ingredients = new ArrayList<>();
@@ -121,43 +117,6 @@ public class BookmarkRecipeId {
return arr;
}
/**
* Ensures the {@link BookmarkRecipeId#recipetype} and {@link BookmarkRecipeId#position} fields point to a valid
* recipe. This is needed, because the recipe IDs are loaded lazily on first use.
*
* @param recipeHandlers The subset of recipe handlers to search for the recipe
*/
public void updateTargetRecipe(List<? extends IRecipeHandler> recipeHandlers) {
if (this.recipetype == -1 || this.position == -1) {
this.recipetype = 0;
this.position = 0;
if (this.handlerName != null) {
for (int j = 0; j < recipeHandlers.size(); j++) {
IRecipeHandler localHandler = recipeHandlers.get(j);
HandlerInfo localHandlerInfo = GuiRecipeTab.getHandlerInfo(localHandler);
if (localHandlerInfo.getHandlerName().equals(this.handlerName)) {
if (!this.ingredients.isEmpty()) {
for (int i = 0; i < localHandler.numRecipes(); i++) {
if (this.equalsIngredients(localHandler.getIngredientStacks(i))) {
this.recipetype = j;
this.position = i;
break;
}
}
}
break;
}
}
}
}
}
public boolean equals(Object anObject) {
if (this == anObject) {
return true;
@@ -6,6 +6,8 @@ import java.awt.Point;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.function.Function;
import java.util.stream.Collectors;
import net.minecraft.client.Minecraft;
import net.minecraft.client.gui.GuiScreen;
@@ -23,50 +25,76 @@ public class GuiCraftingRecipe extends GuiRecipe<ICraftingHandler> {
public static ArrayList<ICraftingHandler> serialCraftingHandlers = new ArrayList<>();
public static boolean openRecipeGui(String outputId, Object... results) {
return openRecipeGui(outputId, false, false, results);
return createRecipeGui(outputId, true, results) != null;
}
public static boolean openRecipeGui(String outputId, final Boolean overlay, final Boolean shift,
Object... results) {
return createRecipeGui(outputId, overlay, shift, true, results) != null;
}
public static GuiRecipe<?> createRecipeGui(String outputId, final Boolean overlay, final Boolean shift,
final boolean open, Object... results) {
public static GuiRecipe<?> createRecipeGui(String outputId, boolean open, Object... results) {
final Minecraft mc = NEIClientUtils.mc();
final BookmarkRecipeId recipeId;
final BookmarkRecipeId recipeId = "item".equals(outputId)
? getRecipeId(mc.currentScreen, (ItemStack) results[0])
: getCurrentRecipe(mc.currentScreen);
if (overlay && recipeId == null) return null;
if ("item".equals(outputId)) {
recipeId = getRecipeId(mc.currentScreen, (ItemStack) results[0]);
} else if ("recipeId".equals(outputId)) {
recipeId = (BookmarkRecipeId) results[1];
} else {
recipeId = getCurrentRecipe(mc.currentScreen);
}
final ArrayList<ICraftingHandler> handlers = getCraftingHandlers(outputId, results);
if (!handlers.isEmpty()) {
GuiCraftingRecipe gui = new GuiCraftingRecipe(handlers, recipeId);
final GuiCraftingRecipe gui = new GuiCraftingRecipe(handlers, "recipeId".equals(outputId));
if (open) {
mc.displayGuiScreen(gui);
}
if (recipeId != null) {
gui.openTargetRecipe(gui.recipeId);
}
if (overlay) {
gui.overlayRecipe(gui.recipeId.position, shift);
}
gui.openTargetRecipe(recipeId);
return gui;
}
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;
Function<ICraftingHandler, ICraftingHandler> recipeHandlerFunction;
if ("recipeId".equals(outputId)) {
ItemStack stack = (ItemStack) results[0];
BookmarkRecipeId recipeId = (BookmarkRecipeId) results[1];
craftinghandlers = filterByHandlerName(craftinghandlers, recipeId.handlerName);
serialCraftingHandlers = filterByHandlerName(serialCraftingHandlers, recipeId.handlerName);
recipeHandlerFunction = h -> h.getRecipeHandler("item", stack);
} else {
recipeHandlerFunction = h -> h.getRecipeHandler(outputId, results);
}
final RecipeHandlerQuery<ICraftingHandler> recipeQuery = new RecipeHandlerQuery<>(
h -> h.getRecipeHandler(outputId, results),
recipeHandlerFunction,
craftinghandlers,
serialCraftingHandlers,
"Error while looking up crafting recipe",
@@ -76,6 +104,16 @@ public class GuiCraftingRecipe extends GuiRecipe<ICraftingHandler> {
return recipeQuery.runWithProfiling("recipe.concurrent.crafting");
}
private static ArrayList<ICraftingHandler> filterByHandlerName(ArrayList<ICraftingHandler> craftinghandlers,
String handlerName) {
return craftinghandlers.stream().filter(h -> getHandlerName(h).equals(handlerName))
.collect(Collectors.toCollection(ArrayList::new));
}
private static String getHandlerName(ICraftingHandler handler) {
return GuiRecipeTab.getHandlerInfo(handler).getHandlerName();
}
protected static BookmarkRecipeId getRecipeId(GuiScreen gui, ItemStack stackover) {
if (gui instanceof GuiRecipe) {
@@ -97,10 +135,10 @@ public class GuiCraftingRecipe extends GuiRecipe<ICraftingHandler> {
return ItemPanels.bookmarkPanel.getBookmarkRecipeId(stackover);
}
private GuiCraftingRecipe(ArrayList<ICraftingHandler> handlers, BookmarkRecipeId recipeId) {
private GuiCraftingRecipe(ArrayList<ICraftingHandler> handlers, boolean limitToOneRecipe) {
super(NEIClientUtils.mc().currentScreen);
this.limitToOneRecipe = limitToOneRecipe;
this.currenthandlers = handlers;
this.recipeId = recipeId;
}
public static void registerRecipeHandler(ICraftingHandler handler) {
@@ -7,11 +7,12 @@ import java.awt.Rectangle;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.LinkedList;
import java.util.List;
import java.util.Optional;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import java.util.stream.Collectors;
import java.util.stream.IntStream;
import net.minecraft.client.gui.GuiButton;
import net.minecraft.client.gui.GuiScreen;
@@ -21,22 +22,30 @@ 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.fluids.FluidStack;
import org.lwjgl.input.Keyboard;
import org.lwjgl.opengl.GL11;
import codechicken.lib.gui.GuiDraw;
import codechicken.nei.Button;
import codechicken.nei.GuiNEIButton;
import codechicken.nei.LayoutManager;
import codechicken.nei.NEICPH;
import codechicken.nei.NEIClientConfig;
import codechicken.nei.NEIClientUtils;
import codechicken.nei.PositionedStack;
import codechicken.nei.RecipeSearchField;
import codechicken.nei.RestartableTask;
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;
import codechicken.nei.drawable.DrawableResource;
@@ -62,7 +71,9 @@ public abstract class GuiRecipe<H extends IRecipeHandler> extends GuiContainer i
private static final int buttonWidth = 13;
private static final int buttonHeight = 12;
private boolean limitToOneRecipe = false;
public static final List<IRecipeFilterProvider> recipeFilterers = new LinkedList<>();
protected boolean limitToOneRecipe = false;
// Background image
final DrawableResource bgTop = new DrawableBuilder("nei:textures/gui/recipebg.png", 0, BG_TOP_Y, 176, BG_TOP_HEIGHT)
@@ -81,9 +92,9 @@ public abstract class GuiRecipe<H extends IRecipeHandler> extends GuiContainer i
BG_BOTTOM_HEIGHT).build();
public ArrayList<H> currenthandlers = new ArrayList<>();
public int page;
public int recipetype;
public BookmarkRecipeId recipeId;
public ContainerRecipe slotcontainer;
public GuiContainer firstGui;
@@ -100,11 +111,164 @@ public abstract class GuiRecipe<H extends IRecipeHandler> extends GuiContainer i
private final Rectangle area = new Rectangle();
private final GuiRecipeTabs recipeTabs;
private final GuiRecipeCatalyst guiRecipeCatalyst;
private H handler;
private SearchRecipeHandler<H> handler;
private HandlerInfo handlerInfo;
private int yShift = 0;
public static class ItemRecipeFilter implements IRecipeFilter {
public ItemFilter filter;
public ItemRecipeFilter(ItemFilter filter) {
this.filter = filter;
}
@Override
public boolean matches(IRecipeHandler handler, List<PositionedStack> ingredients, PositionedStack result,
List<PositionedStack> others) {
if (matchPositionedStack(ingredients)) {
return true;
}
if (matchPositionedStack(result)) {
return true;
}
if (matchPositionedStack(others)) {
return true;
}
return false;
}
private boolean matchPositionedStack(List<PositionedStack> items) {
for (PositionedStack pStack : items) {
if (matchPositionedStack(pStack)) {
return true;
}
}
return false;
}
private boolean matchPositionedStack(PositionedStack pStack) {
if (pStack == null) return false;
for (ItemStack stack : pStack.items) {
if (filter.matches(stack)) {
return true;
}
}
return false;
}
}
public static class AllMultiRecipeFilter implements IRecipeFilter {
public List<IRecipeFilter> filters;
public AllMultiRecipeFilter(List<IRecipeFilter> filters) {
this.filters = filters;
}
public AllMultiRecipeFilter(IRecipeFilter filters) {
this(Arrays.asList(filters));
}
public AllMultiRecipeFilter() {
this(new ArrayList<>());
}
@Override
public boolean matches(IRecipeHandler handler, List<PositionedStack> ingredients, PositionedStack result,
List<PositionedStack> others) {
for (IRecipeFilter filter : filters) {
try {
if (filter != null && !filter.matches(handler, ingredients, result, others)) return false;
} catch (Exception e) {
NEIClientConfig.logger.error("Exception filtering " + handler + " with " + filter, e);
}
}
return true;
}
}
protected static final RestartableTask updateFilter = new RestartableTask("NEI Recipe Filtering") {
@Override
public void execute() {
final GuiScreen currentScreen = NEIClientUtils.mc().currentScreen;
if (currentScreen instanceof GuiRecipe) {
final GuiRecipe<?> guiRecipe = (GuiRecipe<?>) currentScreen;
final SearchRecipeHandler<?> searchHandler = guiRecipe.handler;
if (searchHandler != null && searchHandler.searchingAvailable()) {
if (GuiRecipe.searchField.text().isEmpty()) {
searchHandler.setSearchIndices(null);
guiRecipe.changePage(0);
} else {
final IRecipeFilter filter = new ItemRecipeFilter(GuiRecipe.searchField.getFilter());
ArrayList<Integer> filtered = searchHandler.getSearchResult(filter);
if (filtered == null) {
stop();
}
if (interrupted()) return;
searchHandler.setSearchIndices(filtered);
guiRecipe.changePage(0);
}
}
}
}
};
protected static final RecipeSearchField searchField = new RecipeSearchField("") {
@Override
protected boolean noResults() {
GuiScreen currentScreen = NEIClientUtils.mc().currentScreen;
return !(currentScreen instanceof GuiRecipe) || ((GuiRecipe<?>) currentScreen).numRecipes() > 0;
}
@Override
public void onTextChange(String oldText) {
updateFilter.restart();
}
};
protected static final Button toggleSearch = new Button() {
@Override
public boolean onButtonPress(boolean rightclick) {
if (rightclick) return false;
if (searchField.isVisible()) {
searchField.setText("");
searchField.setFocus(false);
searchField.setVisible(false);
state = 0;
} else {
searchField.setVisible(true);
searchField.setFocus(true);
state = 2;
}
return true;
}
};
/**
* This will only be true iff height hacking has been configured for the current recipe handler AND we are currently
* within the scope of an active {@link CompatibilityHacks} instance.
@@ -119,7 +283,10 @@ public abstract class GuiRecipe<H extends IRecipeHandler> extends GuiContainer i
this.prevGui = prevgui;
this.firstGuiGeneral = prevgui;
if (prevgui instanceof GuiContainer) this.firstGui = (GuiContainer) prevgui;
if (prevgui instanceof GuiContainer) {
this.firstGui = (GuiContainer) prevgui;
}
if (prevgui instanceof IGuiContainerOverlay) {
this.firstGui = ((IGuiContainerOverlay) prevgui).getFirstScreen();
@@ -164,7 +331,7 @@ public abstract class GuiRecipe<H extends IRecipeHandler> extends GuiContainer i
trueGuiTop = guiTop;
isHeightHackApplied = NEIClientConfig.heightHackHandlerRegex.stream()
.map(pattern -> pattern.matcher(handler.getHandlerId())).anyMatch(Matcher::matches);
.map(pattern -> pattern.matcher(handler.original.getHandlerId())).anyMatch(Matcher::matches);
if (isHeightHackApplied) {
// guiTop is the top edge of the recipe screen on the y-axis. Old recipe handlers expect a single paging
// widget at the top and at the bottom of the recipe screen with a height of 16px each, but GTNH NEI
@@ -211,16 +378,11 @@ public abstract class GuiRecipe<H extends IRecipeHandler> extends GuiContainer i
}
}
@Override
public void initGui() {
initGui(false);
}
@SuppressWarnings("unchecked")
public void initGui(boolean asWidget) {
public void initGui() {
xSize = 176;
ySize = Math.min(Math.max(height - 68, 166), 370);
if (!asWidget) {
if (!this.limitToOneRecipe) {
super.initGui();
} else {
this.guiLeft = (this.width - this.xSize) / 2;
@@ -234,11 +396,14 @@ public abstract class GuiRecipe<H extends IRecipeHandler> extends GuiContainer i
this.width = scaledresolution.getScaledWidth();
this.height = scaledresolution.getScaledHeight();
}
guiTop = (height - ySize) / 2 + 10;
currenthandlers = getCurrentRecipeHandlers(); // Probably don't comment me out
if (handler == null) setRecipePage(recipetype);
else initOverlayButtons();
guiTop = (height - ySize) / 2 + 10;
if (handler == null) {
setRecipePage(recipetype);
} else {
initOverlayButtons();
}
checkYShift();
@@ -251,6 +416,9 @@ public abstract class GuiRecipe<H extends IRecipeHandler> extends GuiContainer i
nextpage = new GuiNEIButton(2, leftButtonX, guiTop + 17, buttonWidth, buttonHeight, "<");
prevpage = new GuiNEIButton(3, rightButtonX, guiTop + 17, buttonWidth, buttonHeight, ">");
toggleSearch.icon = new DrawableBuilder("nei:textures/nei_sprites.png", 0, 38, 8, 8).setTextureSize(128, 128)
.build();
buttonList.addAll(Arrays.asList(nexttype, prevtype, nextpage, prevpage));
if (currenthandlers.size() == 1) {
@@ -293,6 +461,25 @@ public abstract class GuiRecipe<H extends IRecipeHandler> extends GuiContainer i
itemPresenceCacheRecipe = -1;
}
public static IRecipeFilter getRecipeListFilter() {
if (recipeFilterers.isEmpty()) {
return null;
}
final AllMultiRecipeFilter recipeFilter = new AllMultiRecipeFilter();
synchronized (recipeFilterers) {
for (IRecipeFilterProvider p : recipeFilterers) {
IRecipeFilter filter = p.getFilter();
if (filter != null) {
recipeFilter.filters.add(filter);
}
}
}
return recipeFilter.filters.size() == 1 ? recipeFilter.filters.get(0) : recipeFilter;
}
private void checkYShift() {
yShift = handlerInfo == null ? 0 : handlerInfo.getYShift();
}
@@ -301,14 +488,20 @@ public abstract class GuiRecipe<H extends IRecipeHandler> extends GuiContainer i
setRecipePage(idx, 0);
}
public void setRecipePage(int idx, int position) {
recipetype = idx;
if (recipetype < 0) recipetype = currenthandlers.size() - 1;
else if (recipetype >= currenthandlers.size()) recipetype = 0;
public void setRecipePage(int idx, int refIndex) {
recipetype = (currenthandlers.size() + idx) % currenthandlers.size();
handler = currenthandlers.get(recipetype);
handlerInfo = GuiRecipeTab.getHandlerInfo(handler);
page = Math.min(Math.max(0, position), handler.numRecipes() - 1) / getRecipesPerPage();
handler = new SearchRecipeHandler<>(currenthandlers.get(recipetype));
handlerInfo = GuiRecipeTab.getHandlerInfo(handler.original);
if (!limitToOneRecipe) {
searchField.setText("");
searchField.setVisible(false);
toggleSearch.state = 0;
}
page = Math.min(Math.max(0, refIndex), this.numRecipes() - 1) / getRecipesPerPage();
changePage(0);
recipeTabs.calcPageNumber();
checkYShift();
initOverlayButtons();
@@ -318,22 +511,42 @@ public abstract class GuiRecipe<H extends IRecipeHandler> extends GuiContainer i
return Collections.unmodifiableList(Arrays.asList(overlayButtons));
}
public void openTargetRecipe(BookmarkRecipeId recipeId) {
public int openTargetRecipe(BookmarkRecipeId recipeId) {
int refIndex = -1;
int recipetype = 0;
if (recipeId == null) {
return;
this.recipeId = recipeId;
if (this.recipeId != null) {
for (int j = 0; j < currenthandlers.size(); j++) {
H localHandler = currenthandlers.get(j);
HandlerInfo localHandlerInfo = GuiRecipeTab.getHandlerInfo(localHandler);
if (localHandlerInfo.getHandlerName().equals(this.recipeId.handlerName)) {
recipetype = j;
if (!this.recipeId.ingredients.isEmpty()) {
refIndex = SearchRecipeHandler.findFirst(
localHandler,
(recipeIndex) -> this.recipeId
.equalsIngredients(localHandler.getIngredientStacks(recipeIndex)));
}
break;
}
}
}
recipeId.updateTargetRecipe(currenthandlers);
setRecipePage(recipeId.recipetype, recipeId.position);
setRecipePage(recipetype, Math.max(0, refIndex));
return refIndex;
}
public List<PositionedStack> getFocusedRecipeIngredients() {
final int recipesPerPage = getRecipesPerPage();
for (int idx = page * recipesPerPage; idx < handler.numRecipes() && idx < (page + 1) * recipesPerPage; idx++) {
if (recipeInFocus(idx)) {
return handler.getIngredientStacks(idx);
for (int recipeIndex : getRecipeIndices()) {
if (recipeInFocus(recipeIndex)) {
return handler.original.getIngredientStacks(recipeIndex);
}
}
@@ -341,18 +554,17 @@ public abstract class GuiRecipe<H extends IRecipeHandler> extends GuiContainer i
}
public int prepareFocusedRecipeResultStackSize(ItemStack stackover) {
final int recipesPerPage = getRecipesPerPage();
for (int idx = page * recipesPerPage; idx < handler.numRecipes() && idx < (page + 1) * recipesPerPage; idx++) {
if (recipeInFocus(idx)) {
final PositionedStack result = handler.getResultStack(idx);
for (int recipeIndex : getRecipeIndices()) {
if (recipeInFocus(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.getOtherStacks(idx);
final List<PositionedStack> stacks = handler.original.getOtherStacks(recipeIndex);
for (PositionedStack pStack : stacks) {
if (StackInfo.equalItemAndNBT(pStack.item, stackover, true)) {
stackSize += pStack.item.stackSize;
@@ -366,15 +578,15 @@ public abstract class GuiRecipe<H extends IRecipeHandler> extends GuiContainer i
return stackover.stackSize;
}
protected boolean recipeInFocus(int idx) {
final PositionedStack result = handler.getResultStack(idx);
if (result != null && isMouseOver(result, idx)) {
protected boolean recipeInFocus(int recipeIndex) {
final PositionedStack result = handler.original.getResultStack(recipeIndex);
if (result != null && isMouseOver(result, recipeIndex)) {
return true;
}
final List<PositionedStack> stacks = handler.getOtherStacks(idx);
final List<PositionedStack> stacks = handler.original.getOtherStacks(recipeIndex);
for (PositionedStack stack : stacks) {
if (isMouseOver(stack, idx)) {
if (isMouseOver(stack, recipeIndex)) {
return true;
}
}
@@ -387,29 +599,54 @@ public abstract class GuiRecipe<H extends IRecipeHandler> extends GuiContainer i
}
public H getHandler() {
return handler;
return handler.original;
}
public List<Integer> getRecipeIndices() {
final int recipesPerPage = getRecipesPerPage();
int minIndex = page * recipesPerPage;
int maxIndex = Math.min(handler.numRecipes(), (page + 1) * recipesPerPage);
final int minIndex = page * recipesPerPage;
final int maxIndex = Math.min(this.numRecipes(), (page + 1) * recipesPerPage);
final ArrayList<Integer> range = new ArrayList<>();
return IntStream.range(minIndex, maxIndex).boxed().collect(Collectors.toList());
for (int index = minIndex; index < maxIndex; index++) {
range.add(handler.ref(index));
}
return range;
}
private int numRecipes() {
return handler == null ? 0 : handler.numRecipes();
}
@Override
public void keyTyped(char c, int i) {
if (i == Keyboard.KEY_ESCAPE) // esc
{
if (searchField.isVisible() && searchField.focused() && searchField.handleKeyPress(i, c)) {
return;
}
if (i == Keyboard.KEY_ESCAPE) { // esc
mc.displayGuiScreen(firstGuiGeneral);
NEICPH.sendRequestContainer();
return;
}
if (GuiContainerManager.getManager(this).lastKeyTyped(i, c)) return;
if (searchField.isVisible() && searchField.focused()) {
searchField.lastKeyTyped(i, c);
return;
}
if (GuiContainerManager.getManager(this).lastKeyTyped(i, c)) {
return;
}
try (CompatibilityHacks compatibilityHacks = new CompatibilityHacks()) {
for (int recipe : getRecipeIndices()) if (handler.keyTyped(this, c, i, recipe)) return;
for (int recipeIndex : getRecipeIndices()) {
if (handler.original.keyTyped(this, c, i, recipeIndex)) {
return;
}
}
}
if (i == mc.gameSettings.keyBindInventory.getKeyCode()) {
@@ -429,18 +666,35 @@ public abstract class GuiRecipe<H extends IRecipeHandler> extends GuiContainer i
}
@Override
protected void mouseClicked(int x, int y, int button) {
try (CompatibilityHacks compatibilityHacks = new CompatibilityHacks()) {
for (int recipe : getRecipeIndices()) if (handler.mouseClicked(this, button, recipe)) return;
protected void mouseClicked(int mousex, int mousey, int button) {
if (!limitToOneRecipe && handler != null && handler.searchingAvailable()) {
if (toggleSearch.contains(mousex - guiLeft, mousey - guiTop)) {
toggleSearch.handleClick(mousex - guiLeft, mousey - guiTop, button);
} else if (searchField.contains(mousex - guiLeft, mousey - guiTop)) {
searchField.handleClick(mousex - guiLeft, mousey - guiTop, button);
} else {
searchField.onGuiClick(mousex - guiLeft, mousey - guiTop);
}
}
if (recipeTabs.mouseClicked(x, y, button)) return;
try (CompatibilityHacks compatibilityHacks = new CompatibilityHacks()) {
for (int recipeIndex : getRecipeIndices()) {
if (handler.original.mouseClicked(this, button, recipeIndex)) {
return;
}
}
}
super.mouseClicked(x, y, button);
if (recipeTabs.mouseClicked(mousex, mousey, button)) {
return;
}
super.mouseClicked(mousex, mousey, button);
}
@Override
public void mouseScrolled(int i) {
public void mouseScrolled(int scroll) {
// Height hacking is not necessary here since mouse scrolling is a new feature, added in
// GTNH NEI. So no old mods will use this. Though not hacking the height here does mean that
// the value of the height field will be different from in other mouseover methods, which
@@ -448,23 +702,33 @@ public abstract class GuiRecipe<H extends IRecipeHandler> extends GuiContainer i
// First, invoke scroll handling over recipe handler tabbar. Makes sure it is not overwritten by recipe
// handler-specific scroll behavior.
if (recipeTabs.mouseScrolled(i)) return;
if (recipeTabs.mouseScrolled(scroll)) return;
for (int recipe : getRecipeIndices()) if (handler.mouseScrolled(this, i, recipe)) return;
for (int recipeIndex : getRecipeIndices()) {
if (handler.original.mouseScrolled(this, scroll, recipeIndex)) {
return;
}
}
// If shift is held, try switching to the next recipe handler. Replicates the GuiRecipeTabs.mouseScrolled()
// without the checking for the cursor being inside the tabbar.
if (NEIClientUtils.shiftKey()) {
if (i < 0) nextType();
else prevType();
if (scroll < 0) {
nextType();
} else {
prevType();
}
return;
}
// Finally, if nothing else has handled scrolling, try changing to the next recipe page.
if (new Rectangle(guiLeft, guiTop, xSize, ySize).contains(GuiDraw.getMousePosition())) {
if (i > 0) prevPage();
else nextPage();
if (scroll > 0) {
prevPage();
} else {
nextPage();
}
}
}
@@ -485,11 +749,16 @@ public abstract class GuiRecipe<H extends IRecipeHandler> extends GuiContainer i
nextPage();
return;
}
if (overlayButtons != null && guibutton.id >= OVERLAY_BUTTON_ID_START
&& guibutton.id < OVERLAY_BUTTON_ID_START + overlayButtons.length) {
overlayRecipe(
page * getRecipesPerPage() + guibutton.id - OVERLAY_BUTTON_ID_START,
NEIClientUtils.shiftKey());
final List<Integer> indices = getRecipeIndices();
final int refIndex = guibutton.id - OVERLAY_BUTTON_ID_START;
if (refIndex >= 0 && refIndex < indices.size()) {
mc.displayGuiScreen(firstGui);
overlayRecipe(indices.get(refIndex), NEIClientUtils.shiftKey());
}
}
}
@@ -501,24 +770,43 @@ public abstract class GuiRecipe<H extends IRecipeHandler> extends GuiContainer i
/** updateScreen() that's safe to call when this isn't the currently open GUI */
public void updateAsTooltip() {
handler.onUpdate();
handler.original.onUpdate();
refreshPage();
}
@Override
public List<String> handleTooltip(GuiContainer gui, int mousex, int mousey, List<String> currenttip) {
try (CompatibilityHacks compatibilityHacks = new CompatibilityHacks()) {
for (int i : getRecipeIndices()) currenttip = handler.handleTooltip(this, currenttip, i);
for (int recipeIndex : getRecipeIndices()) {
currenttip = handler.original.handleTooltip(this, currenttip, recipeIndex);
}
}
recipeTabs.handleTooltip(mousex, mousey, currenttip);
if (currenttip.isEmpty() && searchField.isVisible()
&& new Rectangle(searchField.x + searchField.w, 15, 44, 16)
.contains(mousex - guiLeft, mousey - guiTop)) {
final String s = String.format("%d/%d", page + 1, (this.numRecipes() - 1) / getRecipesPerPage() + 1);
if (fontRendererObj.getStringWidth(s) >= 45) {
currenttip.add(s);
}
}
return currenttip;
}
@Override
public List<String> handleItemTooltip(GuiContainer gui, ItemStack stack, int mousex, int mousey,
List<String> currenttip) {
try (CompatibilityHacks compatibilityHacks = new CompatibilityHacks()) {
for (int i : getRecipeIndices()) currenttip = handler.handleItemTooltip(this, stack, currenttip, i);
for (int recipeIndex : getRecipeIndices()) {
currenttip = handler.original.handleItemTooltip(this, stack, currenttip, recipeIndex);
}
}
return currenttip;
@@ -530,13 +818,11 @@ public abstract class GuiRecipe<H extends IRecipeHandler> extends GuiContainer i
}
private void nextPage() {
page++;
if (page > (handler.numRecipes() - 1) / getRecipesPerPage()) page = 0;
changePage(1);
}
private void prevPage() {
page--;
if (page < 0) page = (handler.numRecipes() - 1) / getRecipesPerPage();
changePage(-1);
}
protected void nextType() {
@@ -547,32 +833,46 @@ public abstract class GuiRecipe<H extends IRecipeHandler> extends GuiContainer i
setRecipePage(--recipetype);
}
protected void overlayRecipe(int recipe, final boolean shift) {
boolean moveItems = shift || !NEIClientConfig.requireShiftForOverlayRecipe();
if (handler == null || !handler.hasOverlay(firstGui, firstGui.inventorySlots, recipe)) {
mc.displayGuiScreen(firstGui);
protected void overlayRecipe(int recipeIndex, final boolean shift) {
if (handler == null || !handler.original.hasOverlay(firstGui, firstGui.inventorySlots, recipeIndex)) {
return;
}
final IRecipeOverlayRenderer renderer = handler.getOverlayRenderer(firstGui, recipe);
final IOverlayHandler overlayHandler = handler.getOverlayHandler(firstGui, recipe);
mc.displayGuiScreen(firstGui);
final boolean moveItems = shift || !NEIClientConfig.requireShiftForOverlayRecipe();
final IRecipeOverlayRenderer renderer = handler.original.getOverlayRenderer(firstGui, recipeIndex);
final IOverlayHandler overlayHandler = handler.original.getOverlayHandler(firstGui, recipeIndex);
if (renderer == null || moveItems) {
if (overlayHandler != null) {
overlayHandler.overlayRecipe(firstGui, currenthandlers.get(recipetype), recipe, moveItems);
overlayHandler.overlayRecipe(firstGui, handler.original, recipeIndex, moveItems);
}
} else {
LayoutManager.overlayRenderer = renderer;
}
}
protected void changePage(int shift) {
final int recipesPerPage = getRecipesPerPage();
final int numRecipes = this.numRecipes();
if (numRecipes > 0) {
final int numPages = (int) Math.ceil(numRecipes / (float) recipesPerPage);
this.page = Math.min(Math.max(0, this.page), numPages);
this.page = (numPages + this.page + shift) % numPages;
} else {
this.page = 0;
}
}
public void refreshPage() {
RecipeCatalysts.updatePosition(ySize - BG_TOP_HEIGHT - (GuiRecipeCatalyst.fullBorder * 2));
changePage(0);
refreshSlots();
final int recipesPerPage = getRecipesPerPage();
final boolean multiplepages = handler.numRecipes() > recipesPerPage;
final int numRecipes = Math.min(handler.numRecipes() - (page * recipesPerPage), recipesPerPage);
final boolean multiplepages = this.numRecipes() > recipesPerPage;
final int numRecipes = Math.min(this.numRecipes() - (page * recipesPerPage), recipesPerPage);
area.width = handlerInfo.getWidth();
area.height = handlerInfo.getHeight() * numRecipes;
@@ -580,6 +880,17 @@ public abstract class GuiRecipe<H extends IRecipeHandler> extends GuiContainer i
area.y = guiTop - 4 + yShift;
checkYShift();
if (!limitToOneRecipe) {
toggleSearch.w = toggleSearch.h = 12;
toggleSearch.x = borderPadding + buttonWidth;
toggleSearch.y = 17;
searchField.y = 16;
searchField.x = borderPadding + buttonWidth + toggleSearch.w - 1;
searchField.w = xSize - (borderPadding + buttonWidth) * 2 + 1 - toggleSearch.w - 45;
searchField.h = 14;
}
nextpage.enabled = prevpage.enabled = multiplepages;
if (firstGui == null) {
@@ -587,12 +898,13 @@ public abstract class GuiRecipe<H extends IRecipeHandler> extends GuiContainer i
overlay.visible = false;
}
} else {
final List<Integer> indices = getRecipeIndices();
for (int i = 0; i < overlayButtons.length; i++) {
if (i >= numRecipes) {
if (i >= indices.size()) {
overlayButtons[i].visible = false;
} else {
final int curRecipe = page * recipesPerPage + i;
overlayButtons[i].visible = handler.hasOverlay(firstGui, firstGui.inventorySlots, curRecipe);
overlayButtons[i].visible = handler.original
.hasOverlay(firstGui, firstGui.inventorySlots, indices.get(i));
}
}
}
@@ -602,9 +914,11 @@ public abstract class GuiRecipe<H extends IRecipeHandler> extends GuiContainer i
private void refreshSlots() {
slotcontainer.inventorySlots.clear();
final int recipesPerPage = getRecipesPerPage();
for (int i = page * recipesPerPage; i < handler.numRecipes() && i < (page + 1) * recipesPerPage; i++) {
Point p = getRecipePosition(i);
final List<Integer> indices = getRecipeIndices();
for (int refIndex = 0; refIndex < indices.size(); refIndex++) {
int recipeIndex = indices.get(refIndex);
Point p = getRecipePosition(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.
@@ -612,36 +926,40 @@ public abstract class GuiRecipe<H extends IRecipeHandler> extends GuiContainer i
p.translate(0, 16);
}
List<PositionedStack> stacks = handler.getIngredientStacks(i);
TemplateRecipeHandler.disableCycledIngredients = false;
List<PositionedStack> stacks = handler.original.getIngredientStacks(recipeIndex);
for (PositionedStack stack : stacks) slotcontainer.addSlot(stack, p.x, p.y);
stacks = handler.getOtherStacks(i);
stacks = handler.original.getOtherStacks(recipeIndex);
for (PositionedStack stack : stacks) slotcontainer.addSlot(stack, p.x, p.y);
PositionedStack result = handler.getResultStack(i);
PositionedStack result = handler.original.getResultStack(recipeIndex);
if (result != null) slotcontainer.addSlot(result, p.x, p.y);
if (!limitToOneRecipe) {
List<PositionedStack> catalysts = RecipeCatalysts.getRecipeCatalysts(handler);
List<PositionedStack> catalysts = RecipeCatalysts.getRecipeCatalysts(handler.original);
for (PositionedStack catalyst : catalysts) {
int xOffset = -GuiRecipeCatalyst.ingredientSize + 1;
int yOffset = BG_TOP_Y + GuiRecipeCatalyst.fullBorder;
slotcontainer.addSlot(catalyst, xOffset, yOffset);
}
}
TemplateRecipeHandler.disableCycledIngredients = true;
}
}
private int itemPresenceCacheRecipe = -1;
private ArrayList<Boolean> itemPresenceCacheSlots;
private void updateItemPresenceCache(int recipe) {
private void updateItemPresenceCache(int recipeIndex) {
if (itemPresenceCacheSlots == null) {
itemPresenceCacheSlots = new ArrayList<>();
}
itemPresenceCacheRecipe = recipe;
itemPresenceCacheRecipe = recipeIndex;
itemPresenceCacheSlots.clear();
List<PositionedStack> ingredients = handler.getIngredientStacks(recipe);
List<PositionedStack> ingredients = handler.original.getIngredientStacks(recipeIndex);
ArrayList<ItemStack> invStacks = ((List<Slot>) firstGui.inventorySlots.inventorySlots).stream()
.filter(
s -> s != null && s.getStack() != null
@@ -665,42 +983,73 @@ public abstract class GuiRecipe<H extends IRecipeHandler> extends GuiContainer i
GuiContainerManager.enable2DRender();
final int recipesPerPage = getRecipesPerPage();
final int ySkip = limitToOneRecipe ? 25 : 0;
if (!limitToOneRecipe) {
String s = handler.getRecipeName().trim();
String s = handler.original.getRecipeName().trim();
fontRendererObj.drawStringWithShadow(s, (xSize - fontRendererObj.getStringWidth(s)) / 2, 5, 0xffffff);
s = NEIClientUtils.translate("recipe.page", page + 1, (handler.numRecipes() - 1) / recipesPerPage + 1);
fontRendererObj.drawStringWithShadow(s, (xSize - fontRendererObj.getStringWidth(s)) / 2, 19, 0xffffff);
if (handler.searchingAvailable()) {
toggleSearch.draw(mouseX - guiLeft, mouseY - guiTop);
}
if (searchField.isVisible()) {
searchField.draw(mouseX - guiLeft, mouseY - guiTop);
s = NEIClientUtils.cropText(
fontRendererObj,
String.format("%d/%d", page + 1, (this.numRecipes() - 1) / recipesPerPage + 1),
45);
fontRendererObj.drawStringWithShadow(
s,
searchField.x + searchField.w + (44 - fontRendererObj.getStringWidth(s)) / 2,
19,
0xffffff);
} else {
s = NEIClientUtils.translate("recipe.page", page + 1, (this.numRecipes() - 1) / recipesPerPage + 1);
fontRendererObj.drawStringWithShadow(s, (xSize - fontRendererObj.getStringWidth(s)) / 2, 19, 0xffffff);
}
}
final boolean drawItemPresence = NEIClientConfig.isJEIStyleItemPresenceOverlayVisible();
GL11.glPushMatrix();
GL11.glTranslatef(5, 32 - ySkip + yShift, 0);
try (CompatibilityHacks compatibilityHacks = new CompatibilityHacks()) {
for (int i = page * recipesPerPage; i < handler.numRecipes() && i < (page + 1) * recipesPerPage; i++) {
handler.drawForeground(i);
if (drawItemPresence && (isMouseOverOverlayButton(i - page * recipesPerPage) || limitToOneRecipe)
&& firstGui != null
&& firstGui.inventorySlots != null) {
List<PositionedStack> ingredients = handler.getIngredientStacks(i);
if (itemPresenceCacheRecipe != i || itemPresenceCacheSlots == null
List<Integer> indices = getRecipeIndices();
for (int refIndex = 0; refIndex < indices.size(); refIndex++) {
int recipeIndex = indices.get(refIndex);
handler.original.drawForeground(recipeIndex);
if (drawItemPresence && firstGui != null
&& firstGui.inventorySlots != null
&& (isMouseOverOverlayButton(refIndex) || limitToOneRecipe)) {
List<PositionedStack> ingredients = handler.original.getIngredientStacks(recipeIndex);
if (itemPresenceCacheRecipe != recipeIndex || itemPresenceCacheSlots == null
|| itemPresenceCacheSlots.size() != ingredients.size()) {
updateItemPresenceCache(i);
updateItemPresenceCache(recipeIndex);
}
for (int j = 0; j < ingredients.size(); j++) {
PositionedStack stack = ingredients.get(j);
boolean isPresent = itemPresenceCacheSlots.get(j);
LayoutManager.drawItemPresenceOverlay(stack.relx, stack.rely, isPresent);
}
}
GL11.glTranslatef(0, handlerInfo.getHeight(), 0);
}
}
GL11.glPopMatrix();
}
@Override
public void drawGuiContainerBackgroundLayer(float f, int mouseX, int mouseY) {
final int recipesPerPage = getRecipesPerPage();
final int ySkip = limitToOneRecipe ? 25 : 0;
GL11.glColor4f(1, 1, 1, 1);
@@ -732,10 +1081,12 @@ public abstract class GuiRecipe<H extends IRecipeHandler> extends GuiContainer i
GL11.glPushMatrix();
GL11.glTranslatef(guiLeft + 5, guiTop - ySkip + 32 + yShift, 0);
try (CompatibilityHacks compatibilityHacks = new CompatibilityHacks()) {
for (int i = page * recipesPerPage; i < handler.numRecipes() && i < (page + 1) * recipesPerPage; i++) {
handler.drawBackground(i);
for (int recipeIndex : getRecipeIndices()) {
handler.original.drawBackground(recipeIndex);
GL11.glTranslatef(0, handlerInfo.getHeight(), 0);
}
}
GL11.glPopMatrix();
}
@@ -781,10 +1132,10 @@ public abstract class GuiRecipe<H extends IRecipeHandler> extends GuiContainer i
return firstGuiGeneral;
}
public boolean isMouseOver(PositionedStack stack, int recipe) {
Slot stackSlot = slotcontainer
.getSlotWithStack(stack, getRecipePosition(recipe).x, getRecipePosition(recipe).y);
public boolean isMouseOver(PositionedStack stack, int refIndex) {
Point p = getRecipePosition(refIndex);
Point mousepos = GuiDraw.getMousePosition();
Slot stackSlot = slotcontainer.getSlotWithStack(stack, p.x, p.y);
Slot mouseoverSlot = getSlotAtPosition(mousepos.x, mousepos.y);
return stackSlot == mouseoverSlot;
@@ -808,13 +1159,19 @@ public abstract class GuiRecipe<H extends IRecipeHandler> extends GuiContainer i
}
private int getRecipesPerPage(HandlerInfo handlerInfo) {
if (handlerInfo != null) return Math.max(
Math.min(((ySize - (buttonHeight * 3)) / handlerInfo.getHeight()), handlerInfo.getMaxRecipesPerPage()),
1);
else return (handler.recipiesPerPage());
if (handlerInfo != null) {
return Math.max(
Math.min(
((ySize - (buttonHeight * 3)) / handlerInfo.getHeight()),
handlerInfo.getMaxRecipesPerPage()),
1);
} else if (handler != null) {
return handler.original.recipiesPerPage();
}
return 1;
}
public Point getRecipePosition(int recipe) {
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.
@@ -822,7 +1179,7 @@ public abstract class GuiRecipe<H extends IRecipeHandler> extends GuiContainer i
5,
(isHeightHackApplied ? 16 : 32) - (limitToOneRecipe ? 25 : 0)
+ yShift
+ ((recipe % getRecipesPerPage()) * handlerInfo.getHeight()));
+ ((refIndex % getRecipesPerPage()) * handlerInfo.getHeight()));
}
public abstract ArrayList<H> getCurrentRecipeHandlers();
@@ -844,9 +1201,27 @@ public abstract class GuiRecipe<H extends IRecipeHandler> extends GuiContainer i
@Override
public boolean handleDragNDrop(GuiContainer gui, int mousex, int mousey, ItemStack draggedStack, int button) {
if (searchField.isVisible() && searchField.contains(mousex - guiLeft, mousey - guiTop)) {
final FluidStack fluidStack = StackInfo.getFluid(draggedStack);
if (fluidStack != null) {
searchField.setText(formattingText(fluidStack.getLocalizedName()));
} else {
searchField.setText(formattingText(draggedStack.getDisplayName()));
}
return true;
}
return false;
}
protected String formattingText(String displayName) {
return Pattern.compile("[{}()\\[\\].+*?^$\\\\|]")
.matcher(EnumChatFormatting.getTextWithoutFormattingCodes(displayName)).replaceAll("\\\\$0");
}
@Override
public boolean hideItemPanelSlot(GuiContainer gui, int x, int y, int w, int h) {
// Because some of the handlers *cough avaritia* are oversized
@@ -855,11 +1230,14 @@ public abstract class GuiRecipe<H extends IRecipeHandler> extends GuiContainer i
protected static BookmarkRecipeId getCurrentRecipe(GuiScreen gui) {
if (gui instanceof GuiRecipe) {
GuiRecipe<?> gRecipe = (GuiRecipe<?>) gui;
if (gui instanceof GuiRecipe && ((GuiRecipe<?>) gui).handler.numRecipes() > 0) {
final GuiRecipe<?> gRecipe = (GuiRecipe<?>) gui;
final List<Integer> indices = gRecipe.getRecipeIndices();
final int curRecipe = indices.isEmpty() ? 0 : indices.get(0);
return new BookmarkRecipeId(
gRecipe.handlerInfo.getHandlerName(),
gRecipe.getHandler().getIngredientStacks(gRecipe.page * gRecipe.getRecipesPerPage()));
gRecipe.handler.original.getIngredientStacks(curRecipe));
}
return null;
@@ -43,7 +43,7 @@ public class GuiRecipeTabs {
categoriesPerPage = 0;
numHandlers = guiRecipe.currenthandlers.size();
for (IRecipeHandler handler : guiRecipe.currenthandlers) {
for (int i = 0; i < numHandlers; i++) {
if (totalWidth + tabWidth < (guiRecipe.xSize - 4)) {
totalWidth += tabWidth;
categoriesPerPage++;
@@ -79,9 +79,11 @@ public class GuiRecipeTabs {
IRecipeHandler handler = guiRecipe.currenthandlers.get(index);
int tabX = area.x + (i * tabWidth);
if (NEIClientConfig.useCreativeTabStyle())
if (NEIClientConfig.useCreativeTabStyle()) {
tabs.add(new GuiRecipeTabCreative(guiRecipe, handler, tabX, area.y));
else tabs.add(new GuiRecipeTabJEI(guiRecipe, handler, tabX, area.y));
} else {
tabs.add(new GuiRecipeTabJEI(guiRecipe, handler, tabX, area.y));
}
}
// Maybe add buttons
@@ -26,19 +26,17 @@ public class GuiUsageRecipe extends GuiRecipe<IUsageHandler> {
Minecraft mc = NEIClientUtils.mc();
BookmarkRecipeId recipeId = getCurrentRecipe(mc.currentScreen);
GuiUsageRecipe gui = new GuiUsageRecipe(handlers, recipeId);
GuiUsageRecipe gui = new GuiUsageRecipe(handlers);
mc.displayGuiScreen(gui);
gui.openTargetRecipe(gui.recipeId);
gui.openTargetRecipe(recipeId);
return true;
}
private GuiUsageRecipe(ArrayList<IUsageHandler> handlers, BookmarkRecipeId recipeId) {
private GuiUsageRecipe(ArrayList<IUsageHandler> handlers) {
super(NEIClientUtils.mc().currentScreen);
this.currenthandlers = handlers;
this.recipeId = recipeId;
}
public static void registerUsageHandler(IUsageHandler handler) {
@@ -52,14 +52,14 @@ public interface IRecipeHandler {
/**
* Draw the background of this recipe handler (basically the slot layout image).
*
*
* @param recipe The recipe index to draw at this position.
*/
void drawBackground(int recipe);
/**
* Draw the foreground of this recipe handler (for things like progress bars).
*
*
* @param recipe The recipe index to draw at this position.
*/
void drawForeground(int recipe);
@@ -74,11 +74,11 @@ public interface IRecipeHandler {
/**
*
* @param recipetype The recipe index to get items for.
* @param recipe The recipe index to get items for.
* @return A list of the other {@link PositionedStack}s in this recipe relative to the top left corner of your
* recipe drawing space. For example fuel in furnaces.
*/
List<PositionedStack> getOtherStacks(int recipetype);
List<PositionedStack> getOtherStacks(int recipe);
/**
*
@@ -80,7 +80,7 @@ public class ProfilerRecipeHandler implements ICraftingHandler, IUsageHandler {
}
@Override
public ArrayList<PositionedStack> getOtherStacks(int recipetype) {
public ArrayList<PositionedStack> getOtherStacks(int recipe) {
return new ArrayList<>();
}
@@ -37,6 +37,7 @@ class RecipeHandlerQuery<T extends IRecipeHandler> {
profiler.start(profilerSection);
try {
ArrayList<T> handlers = getRecipeHandlersParallel();
if (error) {
displayRecipeLookupError();
}
@@ -60,27 +61,37 @@ class RecipeHandlerQuery<T extends IRecipeHandler> {
}
private ArrayList<T> getSerialHandlersWithRecipes() {
return serialRecipeHandlers.stream().map(handler -> {
try {
return recipeHandlerFunction.apply(handler);
return isHidden(handler) ? null : recipeHandlerFunction.apply(handler);
} catch (Throwable t) {
printLog(t);
error = true;
return null;
}
}).filter(h -> h != null && h.numRecipes() > 0).collect(Collectors.toCollection(ArrayList::new));
}).filter(h -> h != null && h.numRecipes() > 0 && SearchRecipeHandler.findFirst(h, (recipeIndex) -> true) != -1)
.collect(Collectors.toCollection(ArrayList::new));
}
private ArrayList<T> getHandlersWithRecipes() throws InterruptedException, ExecutionException {
return ItemList.forkJoinPool.submit(() -> recipeHandlers.parallelStream().map(handler -> {
try {
return recipeHandlerFunction.apply(handler);
return isHidden(handler) ? null : recipeHandlerFunction.apply(handler);
} catch (Throwable t) {
printLog(t);
error = true;
return null;
}
}).filter(h -> h != null && h.numRecipes() > 0).collect(Collectors.toCollection(ArrayList::new))).get();
}).filter(h -> h != null && h.numRecipes() > 0 && SearchRecipeHandler.findFirst(h, (recipeIndex) -> true) != -1)
.collect(Collectors.toCollection(ArrayList::new))).get();
}
private boolean isHidden(T handler) {
final String handlerName = handler.getHandlerId();
final String handlerId = handler.getOverlayIdentifier();
return NEIClientConfig.hiddenHandlers.stream().anyMatch(h -> h.equals(handlerId) || h.equals(handlerName));
}
private void printLog(Throwable t) {
@@ -0,0 +1,120 @@
package codechicken.nei.recipe;
import java.util.ArrayList;
import java.util.concurrent.ExecutionException;
import java.util.function.Predicate;
import java.util.stream.Collectors;
import java.util.stream.IntStream;
import java.util.stream.Stream;
import codechicken.nei.ItemList;
import codechicken.nei.api.IRecipeFilter;
class SearchRecipeHandler<H extends IRecipeHandler> {
public H original;
private ArrayList<Integer> filteredRecipes;
private ArrayList<Integer> searchRecipes;
public SearchRecipeHandler(H handler) {
this.original = handler;
if (this.original.numRecipes() == 0) {
this.filteredRecipes = new ArrayList<>();
} else {
final Stream<Integer> items = IntStream.range(0, this.original.numRecipes()).boxed();
final IRecipeFilter filter = this.searchingAvailable() ? GuiRecipe.getRecipeListFilter() : null;
if (filter == null) {
this.filteredRecipes = items.collect(Collectors.toCollection(ArrayList::new));
} else {
this.filteredRecipes = items.filter(recipe -> mathRecipe(this.original, recipe, filter))
.collect(Collectors.toCollection(ArrayList::new));
}
}
}
protected static boolean mathRecipe(IRecipeHandler handler, int recipe, IRecipeFilter filter) {
return filter.matches(
handler,
handler.getIngredientStacks(recipe),
handler.getResultStack(recipe),
handler.getOtherStacks(recipe));
}
public boolean searchingAvailable() {
return SearchRecipeHandler.searchingAvailable(this.original);
}
private static boolean searchingAvailable(IRecipeHandler handler) {
return handler instanceof TemplateRecipeHandler;
}
public static int findFirst(IRecipeHandler handler, Predicate<Integer> predicate) {
final IRecipeFilter filter = searchingAvailable(handler) ? GuiRecipe.getRecipeListFilter() : null;
int refIndex = -1;
for (int recipeIndex = 0; recipeIndex < handler.numRecipes(); recipeIndex++) {
if (filter == null || mathRecipe(handler, recipeIndex, filter)) {
refIndex++;
if (predicate.test(recipeIndex)) {
return refIndex;
}
}
}
return -1;
}
public ArrayList<Integer> getSearchResult(IRecipeFilter filter) {
if (filteredRecipes.isEmpty() || !this.searchingAvailable()) {
return null;
}
ArrayList<Integer> filtered = null;
final ArrayList<Integer> recipes = IntStream.range(0, filteredRecipes.size()).boxed()
.collect(Collectors.toCollection(ArrayList::new));
try {
filtered = ItemList.forkJoinPool.submit(
() -> recipes.parallelStream()
.filter(recipe -> mathRecipe(this.original, filteredRecipes.get(recipe), filter))
.collect(Collectors.toCollection(ArrayList::new)))
.get();
filtered.sort((a, b) -> a - b);
} catch (InterruptedException | ExecutionException e) {
e.printStackTrace();
}
return filtered;
}
public void setSearchIndices(ArrayList<Integer> searchRecipes) {
this.searchRecipes = searchRecipes;
}
public int ref(int index) {
if (searchRecipes != null) {
index = searchRecipes.get(index);
}
return filteredRecipes.get(index);
}
public int numRecipes() {
if (searchRecipes != null) {
return searchRecipes.size();
}
return filteredRecipes.size();
}
}
@@ -8,14 +8,18 @@ import java.io.InputStreamReader;
import java.net.URL;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.WeakHashMap;
import net.minecraft.item.ItemStack;
import net.minecraft.nbt.NBTBase;
import net.minecraft.nbt.NBTTagCompound;
import net.minecraft.nbt.NBTTagList;
import net.minecraftforge.fluids.FluidContainerRegistry;
import net.minecraftforge.fluids.FluidStack;
import net.minecraftforge.fluids.IFluidContainerItem;
import org.apache.commons.io.IOUtils;
@@ -29,6 +33,13 @@ public class StackInfo {
public static final ArrayList<IStackStringifyHandler> stackStringifyHandlers = new ArrayList<>();
private static final HashMap<String, HashMap<String, String[]>> guidfilters = new HashMap<>();
private static final WeakHashMap<ItemStack, String> guidcache = new WeakHashMap<>();
private static final LinkedHashMap<ItemStack, FluidStack> fluidcache = new LinkedHashMap<ItemStack, FluidStack>() {
@Override
protected boolean removeEldestEntry(Map.Entry<ItemStack, FluidStack> eldest) {
return size() > 20;
}
};
static {
stackStringifyHandlers.add(new DefaultStackStringifyHandler());
@@ -49,11 +60,24 @@ public class StackInfo {
return nbTag;
}
public static ItemStack loadFromNBT(NBTTagCompound nbtTag, long customCount) {
if (nbtTag != null) {
nbtTag = (NBTTagCompound) nbtTag.copy();
nbtTag.setInteger("Count", (int) Math.max(Math.min(customCount, Integer.MAX_VALUE), 0));
return loadFromNBT(nbtTag);
}
return null;
}
public static ItemStack loadFromNBT(NBTTagCompound nbtTag) {
ItemStack stack = null;
for (int i = stackStringifyHandlers.size() - 1; i >= 0 && nbtTag != null && stack == null; i--) {
stack = stackStringifyHandlers.get(i).convertNBTToItemStack(nbtTag);
if (nbtTag != null) {
for (int i = stackStringifyHandlers.size() - 1; i >= 0 && stack == null; i--) {
stack = stackStringifyHandlers.get(i).convertNBTToItemStack(nbtTag);
}
}
return stack;
@@ -76,15 +100,24 @@ public class StackInfo {
}
public static FluidStack getFluid(ItemStack stack) {
FluidStack fluid = null;
FluidStack fluid = fluidcache.get(stack);
for (int i = stackStringifyHandlers.size() - 1; i >= 0 && fluid == null; i--) {
fluid = stackStringifyHandlers.get(i).getFluid(stack);
if (fluid == null && !fluidcache.containsKey(stack)) {
for (int i = stackStringifyHandlers.size() - 1; i >= 0 && fluid == null; i--) {
fluid = stackStringifyHandlers.get(i).getFluid(stack);
}
fluidcache.put(stack, fluid);
}
return fluid;
}
public static boolean isFluidContainer(ItemStack stack) {
return stack.getItem() instanceof IFluidContainerItem || FluidContainerRegistry.isContainer(stack);
}
public static String getItemStackGUID(ItemStack stack) {
if (!guidcache.containsKey(stack)) {
@@ -34,6 +34,8 @@ import com.google.common.base.Stopwatch;
import codechicken.lib.vec.Rectangle4i;
import codechicken.nei.ItemList;
import codechicken.nei.ItemList.AllMultiItemFilter;
import codechicken.nei.LayoutManager;
import codechicken.nei.NEIClientConfig;
import codechicken.nei.NEIClientUtils;
import codechicken.nei.NEIServerUtils;
@@ -42,6 +44,8 @@ import codechicken.nei.api.DefaultOverlayRenderer;
import codechicken.nei.api.IOverlayHandler;
import codechicken.nei.api.IRecipeOverlayRenderer;
import codechicken.nei.api.IStackPositioner;
import codechicken.nei.api.ItemFilter;
import codechicken.nei.api.ItemInfo;
import codechicken.nei.guihook.GuiContainerManager;
import codechicken.nei.guihook.IContainerInputHandler;
import codechicken.nei.guihook.IContainerTooltipHandler;
@@ -174,7 +178,7 @@ public abstract class TemplateRecipeHandler implements ICraftingHandler, IUsageH
/**
* This will perform default cycling of ingredients, mulitItem capable
*
*
* @param cycle Current cycle step
* @param ingredients List of ItemStacks to cycle
* @return The provided list of ingredients, with their permutations cycled to a different permutation, if one
@@ -182,30 +186,53 @@ public abstract class TemplateRecipeHandler implements ICraftingHandler, IUsageH
*/
public List<PositionedStack> getCycledIngredients(int cycle, List<PositionedStack> ingredients) {
if (!NEIClientUtils.shiftKey()) {
if (!NEIClientUtils.shiftKey() && !disableCycledIngredients) {
if (NEIClientConfig.useJEIStyledCycledIngredients()) {
jeiStyledRenderPermutation(ingredients, cycle);
} else {
for (int itemIndex = 0; itemIndex < ingredients.size(); itemIndex++) {
randomRenderPermutation(ingredients.get(itemIndex), cycle + itemIndex);
}
randomRenderPermutation(ingredients, cycle);
}
}
return ingredients;
}
public void randomRenderPermutation(PositionedStack stack, long cycle) {
Random rand = new Random(cycle + offset);
stack.setPermutationToRender(Math.abs(rand.nextInt()) % stack.items.length);
protected void randomRenderPermutation(List<PositionedStack> stacks, long cycle) {
final ItemFilter filter = getItemFilter();
final int randCycle = Math.abs(new Random(cycle + offset).nextInt());
for (PositionedStack stack : stacks) {
if (stack.items.length <= 1) continue;
ArrayList<Integer> filtered = getFilteredIngredientAlternatives(stack, filter);
if (filtered.isEmpty()) {
stack.setPermutationToRender((int) (randCycle % stack.items.length));
} else {
stack.setPermutationToRender(filtered.get((int) (randCycle % filtered.size())));
}
}
}
public void jeiStyledRenderPermutation(List<PositionedStack> stacks, long cycle) {
protected void jeiStyledRenderPermutation(List<PositionedStack> stacks, long cycle) {
final ItemFilter filter = getItemFilter();
for (PositionedStack stack : stacks) {
stack.setPermutationToRender((int) (cycle % stack.items.length));
if (stack.items.length <= 1) continue;
ArrayList<Integer> filtered = getFilteredIngredientAlternatives(stack, filter);
if (filtered.isEmpty()) {
stack.setPermutationToRender((int) (cycle % stack.items.length));
} else {
stack.setPermutationToRender(filtered.get((int) (cycle % filtered.size())));
}
}
}
@Deprecated
protected void randomRenderPermutation(PositionedStack stack, long cycle) {
Random rand = new Random(cycle + offset);
stack.setPermutationToRender((int) (Math.abs(rand.nextInt()) % stack.items.length));
}
public void setIngredientPermutation(Collection<PositionedStack> ingredients, ItemStack ingredient) {
for (PositionedStack stack : ingredients) {
for (int i = 0; i < stack.items.length; i++) {
@@ -396,11 +423,32 @@ public abstract class TemplateRecipeHandler implements ICraftingHandler, IUsageH
*/
public LinkedList<RecipeTransferRect> transferRects = new LinkedList<>();
public static boolean disableCycledIngredients = true;
public TemplateRecipeHandler() {
loadTransferRects();
RecipeTransferRectHandler.registerRectsToGuis(getRecipeTransferRectGuis(), transferRects);
}
protected static ItemFilter getItemFilter() {
return new AllMultiItemFilter(
item -> !ItemInfo.hiddenItems.contains(item),
LayoutManager.presetsPanel != null ? LayoutManager.presetsPanel.getFilter() : null,
GuiRecipe.searchField != null ? GuiRecipe.searchField.getFilter() : null);
}
protected static ArrayList<Integer> getFilteredIngredientAlternatives(PositionedStack stack, ItemFilter filter) {
final ArrayList<Integer> filtered = new ArrayList<>();
for (int i = 0; i < stack.items.length; i++) {
if (filter.matches(stack.items[i])) {
filtered.add(i);
}
}
return filtered;
}
/**
* Add all RecipeTransferRects to the transferRects list during this call. Afterward they may be added to the input
* handler for the corresponding guis from getRecipeTransferRectGuis
@@ -541,7 +589,8 @@ public abstract class TemplateRecipeHandler implements ICraftingHandler, IUsageH
public TemplateRecipeHandler newInstance() {
try {
// Use the non parallel version since we might already be using the forkJoinPool and might have no threads
// Use the non parallel version since we might already be using the forkJoinPool
// and might have no threads
// available
// This will ideally have been pre-cached elsewhere and will be a NOOP
findFuelsOnce();
@@ -680,8 +729,12 @@ public abstract class TemplateRecipeHandler implements ICraftingHandler, IUsageH
@Override
public boolean mouseClicked(GuiRecipe<?> gui, int button, int recipe) {
if (button == 0) return transferRect(gui, recipe, false);
else if (button == 1) return transferRect(gui, recipe, true);
if (button == 0) {
return transferRect(gui, recipe, false);
} else if (button == 1) {
return transferRect(gui, recipe, true);
}
return false;
}
@@ -690,29 +743,58 @@ public abstract class TemplateRecipeHandler implements ICraftingHandler, IUsageH
public boolean mouseScrolled(GuiRecipe<?> gui, int scroll, int recipe) {
if (!NEIClientUtils.shiftKey()) return false;
final Point offset = gui.getRecipePosition(recipe);
final Point pos = getMousePosition();
final Point offset = gui.getRecipePosition(recipe);
final Point relMouse = new Point(pos.x - gui.guiLeft - offset.x, pos.y - gui.guiTop - offset.y);
final PositionedStack overStack = getIngredientMouseOver(relMouse.x, relMouse.y, recipe);
for (PositionedStack pStack : getIngredientStacks(recipe)) {
if ((new Rectangle4i(pStack.relx, pStack.rely, 18, 18)).contains(relMouse.x, relMouse.y)) {
int index = pStack.items.length + scroll;
if (overStack != null && overStack.items.length > 1) {
final ItemFilter filter = getItemFilter();
for (int i = 0; i < pStack.items.length; i++) {
if (NEIServerUtils.areStacksSameTypeCraftingWithNBT(pStack.items[i], pStack.item)) {
index = index + i;
break;
}
if (NEIClientConfig.useJEIStyledCycledIngredients()) {
ItemStack stack = overStack.item;
for (PositionedStack pStack : getIngredientStacks(recipe)) {
shiftPermutationToRender(pStack, stack, scroll, filter);
}
pStack.setPermutationToRender(index % pStack.items.length);
return true;
} else {
shiftPermutationToRender(overStack, overStack.item, scroll, filter);
}
return true;
}
return false;
}
private void shiftPermutationToRender(PositionedStack pStack, ItemStack stack, int scroll, ItemFilter filter) {
for (int index = 0; index < pStack.items.length; index++) {
if (NEIServerUtils.areStacksSameTypeCraftingWithNBT(pStack.items[index], stack)) {
ArrayList<Integer> filtered = getFilteredIngredientAlternatives(pStack, filter);
if (filtered.isEmpty()) {
pStack.setPermutationToRender((int) ((pStack.items.length + scroll + index) % pStack.items.length));
} else {
pStack.setPermutationToRender(
filtered.get((int) ((filtered.size() + scroll + index) % filtered.size())));
}
break;
}
}
}
private PositionedStack getIngredientMouseOver(int mousex, int mousey, int recipe) {
for (PositionedStack pStack : getIngredientStacks(recipe)) {
if ((new Rectangle4i(pStack.relx, pStack.rely, 18, 18)).contains(mousex, mousey)) {
return pStack;
}
}
return null;
}
private boolean transferRect(GuiRecipe<?> gui, int recipe, boolean usage) {
Point offset = gui.getRecipePosition(recipe);
return transferRect(gui, transferRects, offset.x, offset.y, usage);
@@ -21,7 +21,7 @@ public class DefaultStackStringifyHandler implements IStackStringifyHandler {
final NBTTagCompound nbTag = new NBTTagCompound();
nbTag.setString("strId", strId);
nbTag.setInteger("Count", Math.max(saveStackSize ? stack.stackSize : 1, 1));
nbTag.setInteger("Count", saveStackSize ? stack.stackSize : 1);
nbTag.setShort("Damage", (short) stack.getItemDamage());
if (stack.hasTagCompound()) {
@@ -2,6 +2,7 @@ package codechicken.nei.recipe.stackinfo;
import java.lang.reflect.Method;
import net.minecraft.item.Item;
import net.minecraft.item.ItemStack;
import net.minecraft.nbt.NBTTagCompound;
import net.minecraftforge.fluids.Fluid;
@@ -9,16 +10,18 @@ import net.minecraftforge.fluids.FluidRegistry;
import net.minecraftforge.fluids.FluidStack;
import codechicken.nei.api.IStackStringifyHandler;
import cpw.mods.fml.common.registry.GameRegistry;
public class GTFluidStackStringifyHandler implements IStackStringifyHandler {
public static Class GTDisplayFluid = null;
public static Method getFluidDisplayStack = null;
public static Method getFluidFromDisplayStack = null;
protected static Class<?> GTDisplayFluid = null;
protected static Method getFluidDisplayStack = null;
protected static Method getFluidFromDisplayStack = null;
public static boolean replaceAE2FCFluidDrop = false;
static {
try {
final Class gtUtility = Class.forName("gregtech.api.util.GT_Utility");
final Class<?> gtUtility = Class.forName("gregtech.api.util.GT_Utility");
GTDisplayFluid = Class.forName("gregtech.common.items.GT_FluidDisplayItem");
getFluidFromDisplayStack = gtUtility.getMethod("getFluidFromDisplayStack", ItemStack.class);
@@ -31,13 +34,13 @@ public class GTFluidStackStringifyHandler implements IStackStringifyHandler {
public NBTTagCompound convertItemStackToNBT(ItemStack stack, boolean saveStackSize) {
if (GTDisplayFluid != null && GTDisplayFluid.isInstance(stack.getItem())) {
if (replaceAE2FCFluidDrop || stack.getItem() != GameRegistry.findItem("ae2fc", "fluid_drop")) {
final FluidStack fluidStack = getFluid(stack);
if (fluidStack != null) {
final NBTTagCompound nbTag = new NBTTagCompound();
nbTag.setString("gtFluidName", fluidStack.getFluid().getName());
nbTag.setInteger("Count", saveStackSize ? fluidStack.amount : 1);
nbTag.setInteger("Count", saveStackSize ? fluidStack.amount : 144);
return nbTag;
}
}
@@ -66,17 +69,33 @@ public class GTFluidStackStringifyHandler implements IStackStringifyHandler {
}
public FluidStack getFluid(ItemStack stack) {
final Item item = stack.getItem();
if (getFluidFromDisplayStack != null && GTDisplayFluid != null && GTDisplayFluid.isInstance(stack.getItem())) {
try {
try {
if (getFluidFromDisplayStack != null && GTDisplayFluid != null && GTDisplayFluid.isInstance(item)) {
final Object obj = getFluidFromDisplayStack.invoke(null, stack);
if (obj != null) {
return (FluidStack) obj;
}
} catch (Exception e) {}
}
} else if (item == GameRegistry.findItem("ae2fc", "fluid_packet")) {
NBTTagCompound nbtTag = stack.getTagCompound();
return FluidStack.loadFluidStackFromNBT((NBTTagCompound) nbtTag.getTag("FluidStack"));
} else if (item == GameRegistry.findItem("ae2fc", "fluid_drop")) {
NBTTagCompound nbtTag = stack.getTagCompound();
Fluid fluid = FluidRegistry.getFluid(nbtTag.getString("Fluid").toLowerCase());
if (fluid != null) {
FluidStack fluidStack = new FluidStack(fluid, stack.stackSize);
if (nbtTag.hasKey("FluidTag")) {
fluidStack.tag = nbtTag.getCompoundTag("FluidTag");
}
return fluidStack;
}
}
} catch (Exception e) {}
return null;
}
@@ -112,6 +112,7 @@ nei.options.keys.gui.bookmark=Add Bookmark
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
@@ -155,12 +156,11 @@ nei.options.inventory.jei_style_recipe_catalyst.false=No
nei.options.inventory.saveCurrentRecipeInBookmarksEnabled=Save Current Recipe in Bookmarks
nei.options.inventory.saveCurrentRecipeInBookmarksEnabled.true=Enabled
nei.options.inventory.saveCurrentRecipeInBookmarksEnabled.false=Disabled
nei.options.inventory.useNBTInBookmarks=Use NBT in Bookmarks
nei.options.inventory.useNBTInBookmarks.true=Yes
nei.options.inventory.useNBTInBookmarks.false=No
nei.options.inventory.recipeTooltipsEnabled=Show recipe tooltips in Bookmarks
nei.options.inventory.recipeTooltipsEnabled.true=Yes
nei.options.inventory.recipeTooltipsEnabled.false=No
nei.options.inventory.recipeTooltipsMode=Show recipe tooltips in Bookmarks
nei.options.inventory.recipeTooltipsMode.0=None
nei.options.inventory.recipeTooltipsMode.1=In Horizontal View
nei.options.inventory.recipeTooltipsMode.2=In Vertical View
nei.options.inventory.recipeTooltipsMode.3=Always
nei.options.inventory.bookmarksAnimationEnabled=REI Style Animation in Bookmarks
nei.options.inventory.bookmarksAnimationEnabled.true=Yes
nei.options.inventory.bookmarksAnimationEnabled.false=No
@@ -348,6 +348,8 @@ nei.presets.blacklist.label=B
nei.presets.blacklist.tooltip=Blacklist
nei.presets.whitelist.label=W
nei.presets.whitelist.tooltip=Whitelist
nei.presets.blacklistPlus.label=B+
nei.presets.blacklistPlus.tooltip=Blacklist Plus
nei.presets.textfield.tooltip.1=Enter - save
nei.presets.textfield.tooltip.2=Escape - cancel
nei.presets.label.tooltip=Double click to edit
@@ -147,9 +147,6 @@ nei.options.inventory.jei_style_recipe_catalyst.false=否
nei.options.inventory.saveCurrentRecipeInBookmarksEnabled=保存书签中的当前合成表
nei.options.inventory.saveCurrentRecipeInBookmarksEnabled.true=启用
nei.options.inventory.saveCurrentRecipeInBookmarksEnabled.false=禁用
nei.options.inventory.useNBTInBookmarks=在书签中启用NBT
nei.options.inventory.useNBTInBookmarks.true=是
nei.options.inventory.useNBTInBookmarks.false=否
nei.options.inventory.bookmarksAnimationEnabled=REI风格书签动画
nei.options.inventory.bookmarksAnimationEnabled.true=是
nei.options.inventory.bookmarksAnimationEnabled.false=否
@@ -271,7 +268,7 @@ nei.recipe.furnace=烧制
nei.recipe.fuel=燃料
nei.recipe.fuel.smeltCount=可冶炼%s个物品
# Deprecated
nei.recipe.fuel.required=需要%s个
nei.recipe.fuel.required=需要%s个
# Deprecated
nei.recipe.fuel.processed=可烧制%s个
# Deprecated
Binary file not shown.

Before

Width:  |  Height:  |  Size: 37 KiB

After

Width:  |  Height:  |  Size: 34 KiB