From e16be434f50dd47e17900c45c72fd49a19e6e4c7 Mon Sep 17 00:00:00 2001 From: Mikhail Semenov Date: Tue, 21 Oct 2025 21:21:18 +0200 Subject: [PATCH] Add NEI overlay functionality so recipe on anvil can be selected via NEI. This will allow to click plus-sign in NEI and NEI will select recipe on Anvil automatically. --- .../hbm/handler/nei/AnvilOverlayHandler.java | 37 ++++ .../hbm/handler/nei/AnvilRecipeHandler.java | 72 +++++--- .../java/com/hbm/inventory/gui/GUIAnvil.java | 172 ++++++++++-------- src/main/java/com/hbm/main/NEIConfig.java | 5 +- 4 files changed, 185 insertions(+), 101 deletions(-) create mode 100644 src/main/java/com/hbm/handler/nei/AnvilOverlayHandler.java diff --git a/src/main/java/com/hbm/handler/nei/AnvilOverlayHandler.java b/src/main/java/com/hbm/handler/nei/AnvilOverlayHandler.java new file mode 100644 index 000000000..805a9ff09 --- /dev/null +++ b/src/main/java/com/hbm/handler/nei/AnvilOverlayHandler.java @@ -0,0 +1,37 @@ +package com.hbm.handler.nei; + +import com.hbm.inventory.gui.GUIAnvil; +import com.hbm.inventory.recipes.anvil.AnvilRecipes.AnvilConstructionRecipe; + +import codechicken.nei.api.IOverlayHandler; +import codechicken.nei.recipe.IRecipeHandler; +import net.minecraft.client.gui.inventory.GuiContainer; + +/** + * Links NEI overlay interactions with the {@link GUIAnvil} so selecting a recipe focuses the anvil view. + */ +public class AnvilOverlayHandler implements IOverlayHandler { + + /** + * {@inheritDoc} + * + *

When the NEI overlay is opened for the Nuclear Tech anvil, the GUI is instructed to focus the selected recipe.

+ *

It will not craft the recepie. Only select it.

+ * + * @param firstGui the GUI currently displaying the overlay + * @param recipe the NEI recipe handler backing the overlay + * @param recipeIndex index of the recipe within the handler + * @param maxTransfer whether NEI is performing a maximal transfer (ignored) + */ + @Override + public void overlayRecipe(GuiContainer firstGui, IRecipeHandler recipe, int recipeIndex, boolean maxTransfer) { + if(!(firstGui instanceof GUIAnvil) || !(recipe instanceof AnvilRecipeHandler)) { + return; + } + + AnvilConstructionRecipe construction = ((AnvilRecipeHandler) recipe).getConstructionRecipe(recipeIndex); + if(construction != null) { + ((GUIAnvil) firstGui).focusRecipe(construction); + } + } +} diff --git a/src/main/java/com/hbm/handler/nei/AnvilRecipeHandler.java b/src/main/java/com/hbm/handler/nei/AnvilRecipeHandler.java index 2621af6f1..02566ba5e 100644 --- a/src/main/java/com/hbm/handler/nei/AnvilRecipeHandler.java +++ b/src/main/java/com/hbm/handler/nei/AnvilRecipeHandler.java @@ -50,20 +50,25 @@ public class AnvilRecipeHandler extends TemplateRecipeHandler implements ICompat return "ntmAnvil"; } + @Override + public String getOverlayIdentifier() { + return "ntmAnvil"; + } + public LinkedList transferRectsRec = new LinkedList(); public LinkedList transferRectsGui = new LinkedList(); public LinkedList> guiRec = new LinkedList>(); public LinkedList> guiGui = new LinkedList>(); public class RecipeSet extends TemplateRecipeHandler.CachedRecipe { - List input = new ArrayList(); List output = new ArrayList(); PositionedStack anvil; int tier; + final AnvilConstructionRecipe recipe; OverlayType shape; - public RecipeSet(List in, List out, int tier) { + public RecipeSet(List in, List out, int tier, AnvilConstructionRecipe recipe) { //not the prettiest of solutions but certainly the most pleasant to work with int inLine = 1; @@ -74,7 +79,7 @@ public class AnvilRecipeHandler extends TemplateRecipeHandler implements ICompat int outOY = 0; int anvX = 0; int anvY = 31; - + if(in.size() == 1 && out.size() == 1) { shape = OverlayType.SMITHING; inOX = 48; @@ -108,18 +113,19 @@ public class AnvilRecipeHandler extends TemplateRecipeHandler implements ICompat outOY = 6; anvX = 75; } - + for(int i = 0; i < in.size(); i++) { this.input.add(new PositionedStack(in.get(i), inOX + 18 * (i % inLine), inOY + 18 * (i / inLine))); } - + for(int i = 0; i < out.size(); i++) { this.output.add(new PositionedStack(out.get(i), outOX + 18 * (i % outLine), outOY + 18 * (i / outLine))); } - + this.anvil = new PositionedStack(NTMAnvil.getAnvilsFromTier(tier), anvX, anvY); - + this.tier = tier; + this.recipe = recipe; } @Override @@ -148,10 +154,10 @@ public class AnvilRecipeHandler extends TemplateRecipeHandler implements ICompat @Override public void loadCraftingRecipes(String outputId, Object... results) { - + if(outputId.equals("ntmAnvil")) { List recipes = AnvilRecipes.getConstruction(); - + for(AnvilConstructionRecipe recipe : recipes) { this.addRecipeToList(recipe); } @@ -164,9 +170,9 @@ public class AnvilRecipeHandler extends TemplateRecipeHandler implements ICompat public void loadCraftingRecipes(ItemStack result) { List recipes = AnvilRecipes.getConstruction(); - + for(AnvilConstructionRecipe recipe : recipes) { - + for(AnvilOutput out : recipe.output) { if(NEIServerUtils.areStacksSameTypeCrafting(out.stack, result)) { this.addRecipeToList(recipe); @@ -178,7 +184,7 @@ public class AnvilRecipeHandler extends TemplateRecipeHandler implements ICompat @Override public void loadUsageRecipes(String inputId, Object... ingredients) { - + if(inputId.equals("ntmAnvil")) { loadCraftingRecipes("ntmAnvil", new Object[0]); } else { @@ -190,12 +196,12 @@ public class AnvilRecipeHandler extends TemplateRecipeHandler implements ICompat public void loadUsageRecipes(ItemStack ingredient) { List recipes = AnvilRecipes.getConstruction(); - + for(AnvilConstructionRecipe recipe : recipes) { - + outer: for(AStack in : recipe.input) { - + List stacks = in.extractForNEI(); for(ItemStack stack : stacks) { if(NEIServerUtils.areStacksSameTypeCrafting(stack, ingredient)) { @@ -206,38 +212,38 @@ public class AnvilRecipeHandler extends TemplateRecipeHandler implements ICompat } } } - + private void addRecipeToList(AnvilConstructionRecipe recipe) { - + List ins = new ArrayList(); for(AStack input : recipe.input) { ins.add(input.extractForNEI()); } - + List outs = new ArrayList(); for(AnvilOutput output : recipe.output) { - + ItemStack stack = output.stack.copy(); if(output.chance != 1) { ItemStackUtil.addTooltipToStack(stack, EnumChatFormatting.RED + "" + (((int)(output.chance * 1000)) / 10D) + "%"); } - + outs.add(stack); } - - this.arecipes.add(new RecipeSet(ins, outs, recipe.tierLower)); + + this.arecipes.add(new RecipeSet(ins, outs, recipe.tierLower, recipe)); } @Override public void loadTransferRects() { - + //hey asshole, stop nulling my fucking lists transferRectsGui = new LinkedList(); guiGui = new LinkedList>(); transferRectsGui.add(new RecipeTransferRect(new Rectangle(11, 42, 36, 18), "ntmAnvil")); transferRectsGui.add(new RecipeTransferRect(new Rectangle(65, 42, 36, 18), "ntmAnvil")); - + guiGui.add(GUIAnvil.class); RecipeTransferRectHandler.registerRectsToGuis(guiGui, transferRectsGui); } @@ -247,12 +253,26 @@ public class AnvilRecipeHandler extends TemplateRecipeHandler implements ICompat return RefStrings.MODID + ":textures/gui/nei/gui_nei_anvil.png"; } + /** + * Resolves the underlying anvil recipe for the NEI recipe index. + * + * @param index recipe index supplied by NEI + * @return the construction recipe or {@code null} if the index is out of bounds + */ + public AnvilConstructionRecipe getConstructionRecipe(int index) { + if(index < 0 || index >= this.arecipes.size()) { + return null; + } + + return ((RecipeSet) this.arecipes.get(index)).recipe; + } + @Override public void drawBackground(int recipe) { super.drawBackground(recipe); - + RecipeSet set = (RecipeSet) this.arecipes.get(recipe); - + switch(set.shape) { case NONE: drawTexturedModalRect(2, 5, 5, 87, 72, 54); //in diff --git a/src/main/java/com/hbm/inventory/gui/GUIAnvil.java b/src/main/java/com/hbm/inventory/gui/GUIAnvil.java index 9a59e9b29..e3f4c5c84 100644 --- a/src/main/java/com/hbm/inventory/gui/GUIAnvil.java +++ b/src/main/java/com/hbm/inventory/gui/GUIAnvil.java @@ -37,7 +37,7 @@ import net.minecraftforge.oredict.OreDictionary; public class GUIAnvil extends GuiContainer { public static ResourceLocation texture = new ResourceLocation(RefStrings.MODID + ":textures/gui/processing/gui_anvil.png"); - + private int tier; private List originList = new ArrayList(); private List recipes = new ArrayList(); @@ -59,13 +59,13 @@ public class GUIAnvil extends GuiContainer { if(recipe.isTierValid(this.tier)) this.originList.add(recipe); } - + regenerateRecipes(); - + guiLeft = (this.width - this.xSize) / 2; guiTop = (this.height - this.ySize) / 2; } - + @Override public void initGui() { @@ -78,28 +78,52 @@ public class GUIAnvil extends GuiContainer { this.search.setEnableBackgroundDrawing(false); this.search.setMaxStringLength(25); } - + + /** + * Requests the GUI to focus on the supplied recipe. Used by the NEI overlay handler when a recipe is selected externally. + * + * @param target the recipe instance to focus, may be {@code null} + */ + public void focusRecipe(AnvilConstructionRecipe target) { + if(target == null) { + return; + } + if (!this.originList.contains(target)) { + return; + } + search.setText(""); + regenerateRecipes(); + + int pos = this.recipes.indexOf(target); + if(pos < 0) { + return; + } + + this.selection = pos; + this.index = MathHelper.clamp_int(pos / 2, 0, this.size); + } + private void regenerateRecipes() { - + this.recipes.clear(); this.recipes.addAll(this.originList); - + resetPaging(); } - + private void search(String search) { - + search = search.toLowerCase(Locale.US); this.recipes.clear(); - + if(search.isEmpty()) { this.recipes.addAll(this.originList); - + } else { for(AnvilConstructionRecipe recipe : this.originList) { List list = recipeToSearchList(recipe); - + for(String s : list) { if(s.contains(search)) { this.recipes.add(recipe); @@ -108,24 +132,24 @@ public class GUIAnvil extends GuiContainer { } } } - + resetPaging(); } - + private void resetPaging() { - + this.index = 0; this.selection = -1; this.size = Math.max(0, (int)Math.ceil((this.recipes.size() - 10) / 2D)); } - + @Override public void drawScreen(int x, int y, float interp) { super.drawScreen(x, y, interp); - + for(Object obj : this.inventorySlots.inventorySlots) { Slot slot = (Slot) obj; - + // if the mouse is over a slot, cancel if(this.func_146978_c(slot.xDisplayPosition, slot.yDisplayPosition, 16, 16, x, y) && slot.getHasStack()) { return; @@ -135,13 +159,13 @@ public class GUIAnvil extends GuiContainer { if(guiLeft <= x && guiLeft + xSize > x && guiTop < y && guiTop + ySize >= y && getSlotAtPosition(x, y) == null) { if(!Mouse.isButtonDown(0) && !Mouse.isButtonDown(1) && Mouse.next()) { int scroll = Mouse.getEventDWheel(); - + if(scroll > 0 && this.index > 0) this.index--; if(scroll < 0 && this.index < this.size) this.index++; } } } - + private Slot getSlotAtPosition(int x, int y) { for(int k = 0; k < this.inventorySlots.inventorySlots.size(); ++k) { Slot slot = (Slot) this.inventorySlots.inventorySlots.get(k); @@ -153,91 +177,91 @@ public class GUIAnvil extends GuiContainer { return null; } - + private boolean isMouseOverSlot(Slot slot, int x, int y) { return this.func_146978_c(slot.xDisplayPosition, slot.yDisplayPosition, 16, 16, x, y); } - + @Override protected void mouseClicked(int x, int y, int k) { super.mouseClicked(x, y, k); - + this.search.mouseClicked(x, y, k); - + if(guiLeft + 7 <= x && guiLeft + 7 + 9 > x && guiTop + 71 < y && guiTop + 71 + 36 >= y) { mc.getSoundHandler().playSound(PositionedSoundRecord.func_147674_a(new ResourceLocation("gui.button.press"), 1.0F)); if(this.index > 0) this.index--; - + return; } - + if(guiLeft + 106 <= x && guiLeft + 106 + 9 > x && guiTop + 71 < y && guiTop + 71 + 36 >= y) { mc.getSoundHandler().playSound(PositionedSoundRecord.func_147674_a(new ResourceLocation("gui.button.press"), 1.0F)); if(this.index < this.size) this.index++; - + return; } - + if(guiLeft + 52 <= x && guiLeft + 52 + 18 > x && guiTop + 53 < y && guiTop + 53 + 18 >= y) { - + if(this.selection == -1) return; - + mc.getSoundHandler().playSound(PositionedSoundRecord.func_147674_a(new ResourceLocation("gui.button.press"), 1.0F)); PacketDispatcher.wrapper.sendToServer(new AnvilCraftPacket(this.recipes.get(this.selection), Keyboard.isKeyDown(Keyboard.KEY_LSHIFT) ? 1 : 0)); - + return; } - + /*if(guiLeft + 97 <= x && guiLeft + 97 + 18 > x && guiTop + 107 < y && guiTop + 107 + 18 >= y) { mc.getSoundHandler().playSound(PositionedSoundRecord.func_147674_a(new ResourceLocation("gui.button.press"), 1.0F)); search(this.search.getText()); return; }*/ - + for(int i = index * 2; i < index * 2 + 10; i++) { - + if(i >= this.recipes.size()) break; - + int ind = i - index * 2; - + int ix = 16 + 18 * (ind / 2); int iy = 71 + 18 * (ind % 2); if(guiLeft + ix <= x && guiLeft + ix + 18 > x && guiTop + iy < y && guiTop + iy + 18 >= y) { - + if(this.selection != i) this.selection = i; else this.selection = -1; - + mc.getSoundHandler().playSound(PositionedSoundRecord.func_147674_a(new ResourceLocation("gui.button.press"), 1.0F)); return; } } } - + @Override protected void drawGuiContainerForegroundLayer(int mX, int mY) { String name = I18n.format("container.anvil", tier); this.fontRendererObj.drawString(name, 61 - this.fontRendererObj.getStringWidth(name) / 2, 8, 4210752); this.fontRendererObj.drawString(I18n.format("container.inventory"), 8, this.ySize - 96 + 2, 4210752); - + if(this.selection >= 0) { - + AnvilConstructionRecipe recipe = recipes.get(this.selection); List list = recipeToList(recipe, playerInventory); int longest = 0; - + for(String s : list) { int length = this.fontRendererObj.getStringWidth(s); - + if(length > longest) longest = length; } - + double scale = 0.5D; GL11.glScaled(scale, scale, scale); int offset = 0; @@ -245,15 +269,15 @@ public class GUIAnvil extends GuiContainer { this.fontRendererObj.drawString(s, 260, 50 + offset, 0xffffff); offset += 9; } - + this.lastSize = (int)(longest * scale); GL11.glScaled(1D/scale, 1D/scale, 1D/scale); - + } else { this.lastSize = 0; } } - + /** * Generates the neat structured list for showing ingredients * @param recipe @@ -323,7 +347,7 @@ public class GUIAnvil extends GuiContainer { return list; } - + /** * Generates a simple, unstructured list of inputs (and all ore dict variants) and outputs for searching * @param recipe @@ -332,16 +356,16 @@ public class GUIAnvil extends GuiContainer { public List recipeToSearchList(AnvilConstructionRecipe recipe) { List list = new ArrayList(); - + for(AStack stack : recipe.input) { if(stack instanceof ComparableStack) { ItemStack input = ((ComparableStack) stack).toStack(); try { list.add(input.getDisplayName().toLowerCase(Locale.US)); } catch(Exception ex) { list.add("I AM ERROR"); } - + } else if(stack instanceof OreDictStack) { OreDictStack input = (OreDictStack) stack; ArrayList ores = OreDictionary.getOres(input.name); - + if(ores.size() > 0) { for(ItemStack ore : ores) { try { list.add(ore.getDisplayName().toLowerCase(Locale.US)); } catch(Exception ex) { list.add("I AM ERROR"); } @@ -349,44 +373,44 @@ public class GUIAnvil extends GuiContainer { } } } - + for(AnvilOutput stack : recipe.output) { list.add(stack.stack.getDisplayName().toLowerCase(Locale.US)); } - + return list; } - + int lastSize = 1; - + @Override protected void drawGuiContainerBackgroundLayer(float inter, int mX, int mY) { - + GL11.glColor4f(1.0F, 1.0F, 1.0F, 1.0F); this.mc.getTextureManager().bindTexture(texture); - + this.drawTexturedModalRect(guiLeft, guiTop, 0, 0, this.xSize, this.ySize); - + int slide = MathHelper.clamp_int(this.lastSize - 42, 0, 1000); - + int mul = 1; while(true) { - + if(slide >= 51 * mul) { this.drawTexturedModalRect(guiLeft + 125 + 51 * mul, guiTop + 17, 125, 17, 54, 108); mul++; - + } else { break; } } - + this.drawTexturedModalRect(guiLeft + 125 + slide, guiTop + 17, 125, 17, 54, 108); - + if(this.search.isFocused()) { drawTexturedModalRect(guiLeft + 8, guiTop + 108, 168, 222, 88, 16); } - + if(guiLeft + 7 <= mX && guiLeft + 7 + 9 > mX && guiTop + 71 < mY && guiTop + 71 + 36 >= mY) { drawTexturedModalRect(guiLeft + 7, guiTop + 71, 176, 186, 9, 36); } @@ -399,25 +423,25 @@ public class GUIAnvil extends GuiContainer { /*if(guiLeft + 97 <= mX && guiLeft + 97 + 18 > mX && guiTop + 107 < mY && guiTop + 107 + 18 >= mY) { drawTexturedModalRect(guiLeft + 97, guiTop + 107, 176, 168, 18, 18); }*/ - + for(int i = index * 2; i < index * 2 + 10; i++) { if(i >= recipes.size()) break; - + int ind = i - index * 2; - + GL11.glColor4f(1.0F, 1.0F, 1.0F, 1.0F); RenderHelper.enableGUIStandardItemLighting(); GL11.glDisable(GL11.GL_LIGHTING); GL11.glEnable(GL12.GL_RESCALE_NORMAL); - + AnvilConstructionRecipe recipe = recipes.get(i); ItemStack display = recipe.getDisplay(); - + FontRenderer font = null; if (display != null) font = display.getItem().getFontRenderer(display); if (font == null) font = fontRendererObj; - + itemRender.zLevel = 100.0F; itemRender.renderItemAndEffectIntoGUI(font, this.mc.getTextureManager(), recipe.getDisplay(), guiLeft + 17 + 18 * (ind / 2), guiTop + 72 + 18 * (ind % 2)); itemRender.zLevel = 0.0F; @@ -427,17 +451,17 @@ public class GUIAnvil extends GuiContainer { this.mc.getTextureManager().bindTexture(texture); this.zLevel = 300.0F; this.drawTexturedModalRect(guiLeft + 16 + 18 * (ind / 2), guiTop + 71 + 18 * (ind % 2), 18 + 18 * recipe.getOverlay().ordinal(), 222, 18, 18); - + if(selection == i) this.drawTexturedModalRect(guiLeft + 16 + 18 * (ind / 2), guiTop + 71 + 18 * (ind % 2), 0, 222, 18, 18); } - + this.search.drawTextBox(); } - + @Override protected void keyTyped(char c, int key) { - + if(this.search.textboxKeyTyped(c, key)) { search(this.search.getText()); } else { diff --git a/src/main/java/com/hbm/main/NEIConfig.java b/src/main/java/com/hbm/main/NEIConfig.java index 9abd3dfe0..06536e8af 100644 --- a/src/main/java/com/hbm/main/NEIConfig.java +++ b/src/main/java/com/hbm/main/NEIConfig.java @@ -9,8 +9,10 @@ import com.hbm.blocks.ModBlocks; import com.hbm.blocks.generic.BlockPlushie.TileEntityPlushie; import com.hbm.config.ClientConfig; import com.hbm.config.CustomMachineConfigJSON; +import com.hbm.handler.nei.AnvilOverlayHandler; import com.hbm.handler.nei.CustomMachineHandler; import com.hbm.items.ItemEnums.EnumIngotMetal; +import com.hbm.inventory.gui.GUIAnvil; import com.hbm.items.ItemEnums.EnumSecretType; import com.hbm.items.ModItems; import com.hbm.items.machine.ItemBattery; @@ -33,6 +35,7 @@ public class NEIConfig implements IConfigureNEI { for (TemplateRecipeHandler handler: NEIRegistry.listAllHandlers()) { registerHandler(handler); } + API.registerGuiOverlayHandler(GUIAnvil.class, new AnvilOverlayHandler(), "ntmAnvil"); for(CustomMachineConfigJSON.MachineConfiguration conf : CustomMachineConfigJSON.niceList) { registerHandlerBypass(new CustomMachineHandler(conf)); @@ -43,7 +46,7 @@ public class NEIConfig implements IConfigureNEI { for(int i = 0; i < EnumAmmoSecret.values().length; i++) API.hideItem(new ItemStack(ModItems.ammo_secret, 1, i)); for(int i = 0; i < EnumSecretType.values().length; i++) API.hideItem(new ItemStack(ModItems.item_secret, 1, i)); } - + for(int i = 0; i < EnumIngotMetal.values().length; i++) API.hideItem(new ItemStack(ModItems.ingot_metal, 1, i)); //Some things are even beyond my control...or are they?