diff --git a/gradle.properties b/gradle.properties index ca6acaa14..2289583aa 100644 --- a/gradle.properties +++ b/gradle.properties @@ -31,9 +31,9 @@ org.gradle.daemon=false org.gradle.caching=true # Dependencies -cyclopscore_version=1.26.2-808 +cyclopscore_version=1.29.3-1096 integrateddynamics_version=1.32.0-1630 integratedterminalscompat_version=1.0.0-183 -integratedcrafting_version=1.5.0-689 +integratedcrafting_version=1.6.0-715 integratedtunnels_version=1.8.44-484 -commoncapabilities_version=2.9.12-263 +commoncapabilities_version=2.11.5-363 diff --git a/src/main/java/org/cyclops/integratedterminals/api/ingredient/IIngredientComponentTerminalStorageHandler.java b/src/main/java/org/cyclops/integratedterminals/api/ingredient/IIngredientComponentTerminalStorageHandler.java index b984b6913..31a585c44 100644 --- a/src/main/java/org/cyclops/integratedterminals/api/ingredient/IIngredientComponentTerminalStorageHandler.java +++ b/src/main/java/org/cyclops/integratedterminals/api/ingredient/IIngredientComponentTerminalStorageHandler.java @@ -5,6 +5,8 @@ import net.minecraft.world.entity.player.Player; import net.minecraft.world.entity.player.Inventory; import net.minecraft.world.inventory.AbstractContainerMenu; +import com.mojang.datafixers.util.Either; +import net.minecraft.network.chat.FormattedText; import net.minecraft.world.inventory.tooltip.TooltipComponent; import net.minecraft.world.item.ItemStack; import net.minecraft.network.chat.Component; @@ -76,12 +78,13 @@ public default void drawInstance(GuiGraphics guiGraphics, T instance, long maxQu * @param mouseX The mouse X position. * @param mouseY The mouse Y position. * @param additionalTooltipLines The additional tooltip lines to add. - * @param additionalTooltipComponent An optional visual tooltip component to render below the tooltip lines. + * @param additionalTooltipElements Optional elements to render below the tooltip lines, + * which can mix text and visual components. */ @OnlyIn(Dist.CLIENT) public void drawInstance(GuiGraphics guiGraphics, T instance, long maxQuantity, @Nullable String label, AbstractContainerScreen gui, ContainerScreenTerminalStorage.DrawLayer layer, float partialTick, int x, int y, int mouseX, int mouseY, @Nullable List additionalTooltipLines, - @Nullable TooltipComponent additionalTooltipComponent); + @Nullable List> additionalTooltipElements); /** * Show the quantity of the given instance on the second tooltip line. diff --git a/src/main/java/org/cyclops/integratedterminals/api/terminalstorage/crafting/ITerminalCraftingOption.java b/src/main/java/org/cyclops/integratedterminals/api/terminalstorage/crafting/ITerminalCraftingOption.java index 6d581b64f..64b626d2e 100644 --- a/src/main/java/org/cyclops/integratedterminals/api/terminalstorage/crafting/ITerminalCraftingOption.java +++ b/src/main/java/org/cyclops/integratedterminals/api/terminalstorage/crafting/ITerminalCraftingOption.java @@ -1,5 +1,6 @@ package org.cyclops.integratedterminals.api.terminalstorage.crafting; +import net.minecraft.world.item.ItemStack; import org.cyclops.commoncapabilities.api.ingredient.IIngredientMatcher; import org.cyclops.commoncapabilities.api.ingredient.IPrototypedIngredient; import org.cyclops.commoncapabilities.api.ingredient.IngredientComponent; @@ -59,6 +60,15 @@ public interface ITerminalCraftingOption extends Comparable Collection getInputs(IngredientComponent ingredientComponent); + /** + * Item representations of the machines that this crafting job option is crafted in. + * + * @return The crafting machines, can be empty if they are unknown. + */ + public default List getCraftingMachines() { + return Collections.emptyList(); + } + /** * The inputs of this crafting job option for the given ingredient component, * where each input can be fulfilled by any of its alternatives. diff --git a/src/main/java/org/cyclops/integratedterminals/capability/ingredient/IngredientComponentTerminalStorageHandlerEnergy.java b/src/main/java/org/cyclops/integratedterminals/capability/ingredient/IngredientComponentTerminalStorageHandlerEnergy.java index 5cf5c96b0..4fe020c06 100644 --- a/src/main/java/org/cyclops/integratedterminals/capability/ingredient/IngredientComponentTerminalStorageHandlerEnergy.java +++ b/src/main/java/org/cyclops/integratedterminals/capability/ingredient/IngredientComponentTerminalStorageHandlerEnergy.java @@ -10,6 +10,8 @@ import net.minecraft.world.entity.player.Player; import net.minecraft.world.inventory.AbstractContainerMenu; import net.minecraft.world.inventory.Slot; +import com.mojang.datafixers.util.Either; +import net.minecraft.network.chat.FormattedText; import net.minecraft.world.inventory.tooltip.TooltipComponent; import net.minecraft.world.item.ItemStack; import net.neoforged.api.distmarker.Dist; @@ -67,7 +69,7 @@ public ItemStack getIcon() { public void drawInstance(GuiGraphics guiGraphics, Long instance, long maxQuantity, @Nullable String label, AbstractContainerScreen gui, ContainerScreenTerminalStorage.DrawLayer layer, float partialTick, int x, int y, int mouseX, int mouseY, @Nullable List additionalTooltipLines, - @Nullable TooltipComponent additionalTooltipComponent) { + @Nullable List> additionalTooltipElements) { if (instance > 0) { if (layer == ContainerScreenTerminalStorage.DrawLayer.BACKGROUND){ @@ -103,7 +105,7 @@ public void drawInstance(GuiGraphics guiGraphics, Long instance, long maxQuantit lines.addAll(additionalTooltipLines); } return lines; - }, additionalTooltipComponent); + }, additionalTooltipElements); } } } diff --git a/src/main/java/org/cyclops/integratedterminals/capability/ingredient/IngredientComponentTerminalStorageHandlerFluidStack.java b/src/main/java/org/cyclops/integratedterminals/capability/ingredient/IngredientComponentTerminalStorageHandlerFluidStack.java index 8844232f8..8e3cc0212 100644 --- a/src/main/java/org/cyclops/integratedterminals/capability/ingredient/IngredientComponentTerminalStorageHandlerFluidStack.java +++ b/src/main/java/org/cyclops/integratedterminals/capability/ingredient/IngredientComponentTerminalStorageHandlerFluidStack.java @@ -12,6 +12,8 @@ import net.minecraft.world.entity.player.Player; import net.minecraft.world.inventory.AbstractContainerMenu; import net.minecraft.world.inventory.Slot; +import com.mojang.datafixers.util.Either; +import net.minecraft.network.chat.FormattedText; import net.minecraft.world.inventory.tooltip.TooltipComponent; import net.minecraft.world.item.ItemStack; import net.minecraft.world.item.Items; @@ -74,7 +76,7 @@ public void drawInstance(GuiGraphics guiGraphics, FluidStack instance, long maxQ ContainerScreenTerminalStorage.DrawLayer layer, float partialTick, int x, int y, int mouseX, int mouseY, @Nullable List additionalTooltipLines, - @Nullable TooltipComponent additionalTooltipComponent) { + @Nullable List> additionalTooltipElements) { if (instance != null) { if (layer == ContainerScreenTerminalStorage.DrawLayer.BACKGROUND) { // Draw fluid @@ -92,7 +94,7 @@ public void drawInstance(GuiGraphics guiGraphics, FluidStack instance, long maxQ lines.addAll(additionalTooltipLines); } return lines; - }, additionalTooltipComponent); + }, additionalTooltipElements); } } } diff --git a/src/main/java/org/cyclops/integratedterminals/capability/ingredient/IngredientComponentTerminalStorageHandlerItemStack.java b/src/main/java/org/cyclops/integratedterminals/capability/ingredient/IngredientComponentTerminalStorageHandlerItemStack.java index 2d3c4d228..083db7e52 100644 --- a/src/main/java/org/cyclops/integratedterminals/capability/ingredient/IngredientComponentTerminalStorageHandlerItemStack.java +++ b/src/main/java/org/cyclops/integratedterminals/capability/ingredient/IngredientComponentTerminalStorageHandlerItemStack.java @@ -12,6 +12,8 @@ import net.minecraft.world.entity.player.Player; import net.minecraft.world.inventory.AbstractContainerMenu; import net.minecraft.world.inventory.Slot; +import com.mojang.datafixers.util.Either; +import net.minecraft.network.chat.FormattedText; import net.minecraft.world.inventory.tooltip.TooltipComponent; import net.minecraft.world.item.Item; import net.minecraft.world.item.ItemStack; @@ -70,7 +72,7 @@ public ItemStack getIcon() { public void drawInstance(GuiGraphics guiGraphics, ItemStack instance, long maxQuantity, @Nullable String label, AbstractContainerScreen gui, ContainerScreenTerminalStorage.DrawLayer layer, float partialTick, int x, int y, int mouseX, int mouseY, @Nullable List additionalTooltipLines, - @Nullable TooltipComponent additionalTooltipComponent) { + @Nullable List> additionalTooltipElements) { // Make a copy of the item to make sure that any changes in the NBT tag that the mod may make during rendering // does not propagate into our client-side index. Otherwise, the client may think it has different items than // the server, which will cause these items not to be extractable by the client from the terminal. @@ -98,7 +100,7 @@ public void drawInstance(GuiGraphics guiGraphics, ItemStack instance, long maxQu } addQuantityTooltip(lines, instanceCopy); return lines; - }, additionalTooltipComponent); + }, additionalTooltipElements); } Lighting.setupForFlatItems(); } diff --git a/src/main/java/org/cyclops/integratedterminals/client/gui/tooltip/ClientCraftingOptionIngredientsTooltip.java b/src/main/java/org/cyclops/integratedterminals/client/gui/tooltip/ClientCraftingOptionIngredientsTooltip.java index 4563defc7..62e4adb5e 100644 --- a/src/main/java/org/cyclops/integratedterminals/client/gui/tooltip/ClientCraftingOptionIngredientsTooltip.java +++ b/src/main/java/org/cyclops/integratedterminals/client/gui/tooltip/ClientCraftingOptionIngredientsTooltip.java @@ -1,96 +1,34 @@ package org.cyclops.integratedterminals.client.gui.tooltip; -import net.minecraft.client.Minecraft; -import net.minecraft.client.gui.Font; import net.minecraft.client.gui.GuiGraphics; -import net.minecraft.client.gui.screens.Screen; -import net.minecraft.client.gui.screens.inventory.AbstractContainerScreen; -import net.minecraft.client.gui.screens.inventory.tooltip.ClientTooltipComponent; -import net.minecraft.resources.ResourceLocation; import net.neoforged.api.distmarker.Dist; import net.neoforged.api.distmarker.OnlyIn; import org.cyclops.commoncapabilities.api.ingredient.IPrototypedIngredient; -import org.cyclops.commoncapabilities.api.ingredient.IngredientComponent; -import org.cyclops.integratedterminals.Capabilities; -import org.cyclops.integratedterminals.client.gui.container.ContainerScreenTerminalStorage; -import javax.annotation.Nullable; import java.util.List; /** * Renders the ingredients of a {@link CraftingOptionIngredientsTooltip} as a grid of icons. * - * The grid wraps after {@link #MAX_COLUMNS} icons, - * and inputs that accept multiple alternatives are cycled through within their own slot. + * Inputs that accept multiple alternatives are cycled through within their own slot. * * @author rubensworks */ @OnlyIn(Dist.CLIENT) -public class ClientCraftingOptionIngredientsTooltip implements ClientTooltipComponent { - - private static final ResourceLocation SLOT_SPRITE = ResourceLocation.withDefaultNamespace("container/bundle/slot"); - private static final int SLOT_WIDTH = 18; - private static final int SLOT_HEIGHT = 20; - private static final int MAX_COLUMNS = 9; - private static final int MARGIN_Y = 2; - - /** - * The number of ticks an alternative is shown before cycling to the next one. - */ - private static final int TICK_DELAY = 30; +public class ClientCraftingOptionIngredientsTooltip extends ClientCraftingOptionSlotsTooltip { private final List>> ingredients; - private final int columns; - private final int rows; public ClientCraftingOptionIngredientsTooltip(CraftingOptionIngredientsTooltip tooltip) { + super(tooltip.ingredients().size()); this.ingredients = tooltip.ingredients(); - // Spread the ingredients evenly over as few rows as possible - this.rows = Math.max(1, (int) Math.ceil((double) this.ingredients.size() / MAX_COLUMNS)); - this.columns = Math.max(1, (int) Math.ceil((double) this.ingredients.size() / this.rows)); - } - - @Override - public int getHeight() { - return this.rows * SLOT_HEIGHT + MARGIN_Y; - } - - @Override - public int getWidth(Font font) { - return this.columns * SLOT_WIDTH; } @Override - public void renderImage(Font font, int x, int y, GuiGraphics guiGraphics) { - int tick = getTick(); - for (int i = 0; i < this.ingredients.size(); i++) { - List> alternatives = this.ingredients.get(i); - int slotX = x + (i % this.columns) * SLOT_WIDTH; - int slotY = y + (i / this.columns) * SLOT_HEIGHT; - guiGraphics.blitSprite(SLOT_SPRITE, slotX, slotY, SLOT_WIDTH, SLOT_HEIGHT); - // Cycle over the alternatives of this input - drawIngredient(guiGraphics, alternatives.get(tick % alternatives.size()), slotX + 1, slotY + 1); - } - } - - protected static void drawIngredient(GuiGraphics guiGraphics, IPrototypedIngredient ingredient, int x, int y) { - IngredientComponent ingredientComponent = ingredient.getComponent(); - long quantity = ingredientComponent.getMatcher().getQuantity(ingredient.getPrototype()); - ingredientComponent.getCapability(Capabilities.IngredientComponentTerminalStorageHandler.INGREDIENT) - .ifPresent(handler -> handler.drawInstance(guiGraphics, ingredient.getPrototype(), quantity, null, - getContainerScreen(), ContainerScreenTerminalStorage.DrawLayer.BACKGROUND, 0, - x, y, 0, 0, null)); - } - - @Nullable - protected static AbstractContainerScreen getContainerScreen() { - Screen screen = Minecraft.getInstance().screen; - return screen instanceof AbstractContainerScreen containerScreen ? containerScreen : null; - } - - protected static int getTick() { - Minecraft minecraft = Minecraft.getInstance(); - return minecraft.level == null ? 0 : (int) (minecraft.level.getGameTime() / TICK_DELAY); + protected void drawSlot(GuiGraphics guiGraphics, int slot, int x, int y) { + // Cycle over the alternatives of this input + List> alternatives = this.ingredients.get(slot); + drawIngredient(guiGraphics, alternatives.get(getTick() % alternatives.size()), x, y); } } diff --git a/src/main/java/org/cyclops/integratedterminals/client/gui/tooltip/ClientCraftingOptionMachinesTooltip.java b/src/main/java/org/cyclops/integratedterminals/client/gui/tooltip/ClientCraftingOptionMachinesTooltip.java new file mode 100644 index 000000000..2f43f696e --- /dev/null +++ b/src/main/java/org/cyclops/integratedterminals/client/gui/tooltip/ClientCraftingOptionMachinesTooltip.java @@ -0,0 +1,30 @@ +package org.cyclops.integratedterminals.client.gui.tooltip; + +import net.minecraft.client.gui.GuiGraphics; +import net.minecraft.world.item.ItemStack; +import net.neoforged.api.distmarker.Dist; +import net.neoforged.api.distmarker.OnlyIn; + +import java.util.List; + +/** + * Renders the machines of a {@link CraftingOptionMachinesTooltip} as a grid of icons. + * + * @author rubensworks + */ +@OnlyIn(Dist.CLIENT) +public class ClientCraftingOptionMachinesTooltip extends ClientCraftingOptionSlotsTooltip { + + private final List machines; + + public ClientCraftingOptionMachinesTooltip(CraftingOptionMachinesTooltip tooltip) { + super(tooltip.machines().size()); + this.machines = tooltip.machines(); + } + + @Override + protected void drawSlot(GuiGraphics guiGraphics, int slot, int x, int y) { + guiGraphics.renderItem(this.machines.get(slot), x, y); + } + +} diff --git a/src/main/java/org/cyclops/integratedterminals/client/gui/tooltip/ClientCraftingOptionSlotsTooltip.java b/src/main/java/org/cyclops/integratedterminals/client/gui/tooltip/ClientCraftingOptionSlotsTooltip.java new file mode 100644 index 000000000..4ac80353c --- /dev/null +++ b/src/main/java/org/cyclops/integratedterminals/client/gui/tooltip/ClientCraftingOptionSlotsTooltip.java @@ -0,0 +1,100 @@ +package org.cyclops.integratedterminals.client.gui.tooltip; + +import net.minecraft.client.Minecraft; +import net.minecraft.client.gui.Font; +import net.minecraft.client.gui.GuiGraphics; +import net.minecraft.client.gui.screens.Screen; +import net.minecraft.client.gui.screens.inventory.AbstractContainerScreen; +import net.minecraft.client.gui.screens.inventory.tooltip.ClientTooltipComponent; +import net.minecraft.resources.ResourceLocation; +import net.neoforged.api.distmarker.Dist; +import net.neoforged.api.distmarker.OnlyIn; +import org.cyclops.commoncapabilities.api.ingredient.IPrototypedIngredient; +import org.cyclops.commoncapabilities.api.ingredient.IngredientComponent; +import org.cyclops.integratedterminals.Capabilities; +import org.cyclops.integratedterminals.client.gui.container.ContainerScreenTerminalStorage; + +import javax.annotation.Nullable; + +/** + * Base class for tooltip components that render a grid of slots. + * + * The grid wraps after {@link #MAX_COLUMNS} slots, + * and is spread evenly over as few rows as possible. + * + * @author rubensworks + */ +@OnlyIn(Dist.CLIENT) +public abstract class ClientCraftingOptionSlotsTooltip implements ClientTooltipComponent { + + protected static final ResourceLocation SLOT_SPRITE = ResourceLocation.withDefaultNamespace("container/bundle/slot"); + protected static final int SLOT_WIDTH = 18; + protected static final int SLOT_HEIGHT = 20; + protected static final int MAX_COLUMNS = 9; + protected static final int MARGIN_Y = 2; + + /** + * The number of ticks an alternative is shown before cycling to the next one. + */ + protected static final int TICK_DELAY = 30; + + private final int slots; + private final int columns; + private final int rows; + + public ClientCraftingOptionSlotsTooltip(int slots) { + this.slots = slots; + this.rows = Math.max(1, (int) Math.ceil((double) slots / MAX_COLUMNS)); + this.columns = Math.max(1, (int) Math.ceil((double) slots / this.rows)); + } + + /** + * Draw the contents of the slot at the given index. + * @param guiGraphics The gui graphics. + * @param slot The slot index. + * @param x The X position to draw at. + * @param y The Y position to draw at. + */ + protected abstract void drawSlot(GuiGraphics guiGraphics, int slot, int x, int y); + + @Override + public int getHeight() { + return this.rows * SLOT_HEIGHT + MARGIN_Y; + } + + @Override + public int getWidth(Font font) { + return this.columns * SLOT_WIDTH; + } + + @Override + public void renderImage(Font font, int x, int y, GuiGraphics guiGraphics) { + for (int i = 0; i < this.slots; i++) { + int slotX = x + (i % this.columns) * SLOT_WIDTH; + int slotY = y + (i / this.columns) * SLOT_HEIGHT; + guiGraphics.blitSprite(SLOT_SPRITE, slotX, slotY, SLOT_WIDTH, SLOT_HEIGHT); + drawSlot(guiGraphics, i, slotX + 1, slotY + 1); + } + } + + protected static void drawIngredient(GuiGraphics guiGraphics, IPrototypedIngredient ingredient, int x, int y) { + IngredientComponent ingredientComponent = ingredient.getComponent(); + long quantity = ingredientComponent.getMatcher().getQuantity(ingredient.getPrototype()); + ingredientComponent.getCapability(Capabilities.IngredientComponentTerminalStorageHandler.INGREDIENT) + .ifPresent(handler -> handler.drawInstance(guiGraphics, ingredient.getPrototype(), quantity, null, + getContainerScreen(), ContainerScreenTerminalStorage.DrawLayer.BACKGROUND, 0, + x, y, 0, 0, null)); + } + + @Nullable + protected static AbstractContainerScreen getContainerScreen() { + Screen screen = Minecraft.getInstance().screen; + return screen instanceof AbstractContainerScreen containerScreen ? containerScreen : null; + } + + protected static int getTick() { + Minecraft minecraft = Minecraft.getInstance(); + return minecraft.level == null ? 0 : (int) (minecraft.level.getGameTime() / TICK_DELAY); + } + +} diff --git a/src/main/java/org/cyclops/integratedterminals/client/gui/tooltip/CraftingOptionMachinesTooltip.java b/src/main/java/org/cyclops/integratedterminals/client/gui/tooltip/CraftingOptionMachinesTooltip.java new file mode 100644 index 000000000..4c2bd8b5c --- /dev/null +++ b/src/main/java/org/cyclops/integratedterminals/client/gui/tooltip/CraftingOptionMachinesTooltip.java @@ -0,0 +1,15 @@ +package org.cyclops.integratedterminals.client.gui.tooltip; + +import net.minecraft.world.inventory.tooltip.TooltipComponent; +import net.minecraft.world.item.ItemStack; + +import java.util.List; + +/** + * A tooltip component holding the machines that a crafting option is crafted in. + * + * @param machines The crafting machines. + * @author rubensworks + */ +public record CraftingOptionMachinesTooltip(List machines) implements TooltipComponent { +} diff --git a/src/main/java/org/cyclops/integratedterminals/client/gui/tooltip/TooltipRenderHelpers.java b/src/main/java/org/cyclops/integratedterminals/client/gui/tooltip/TooltipRenderHelpers.java index 60fdc2207..fd933009e 100644 --- a/src/main/java/org/cyclops/integratedterminals/client/gui/tooltip/TooltipRenderHelpers.java +++ b/src/main/java/org/cyclops/integratedterminals/client/gui/tooltip/TooltipRenderHelpers.java @@ -31,9 +31,9 @@ private TooltipRenderHelpers() {} /** * Render a tooltip if the mouse hovers over the given region. * - * If no visual component is given, this is equivalent to {@link GuiHelpers}'s tooltip rendering. + * If no additional elements are given, this is equivalent to {@link GuiHelpers}'s tooltip rendering. * Otherwise, the tooltip is rendered by vanilla, - * so that the visual component can be rendered below the tooltip lines. + * so that visual components can be rendered below the tooltip lines. * * This must be called while rendering the foreground layer of the given gui, * as the given position is expected to be relative to the gui. @@ -47,19 +47,20 @@ private TooltipRenderHelpers() {} * @param mouseX The mouse X position. * @param mouseY The mouse Y position. * @param linesSupplier A supplier of the tooltip lines. - * @param visualComponent An optional visual tooltip component. + * @param additionalElements Optional elements to append below the tooltip lines, + * which can mix text and visual components. */ public static void renderTooltip(AbstractContainerScreen gui, GuiGraphics guiGraphics, int x, int y, int width, int height, int mouseX, int mouseY, Supplier> linesSupplier, - @Nullable TooltipComponent visualComponent) { - if (visualComponent == null) { + @Nullable List> additionalElements) { + if (additionalElements == null || additionalElements.isEmpty()) { GuiHelpers.renderTooltip(gui, guiGraphics.pose(), x, y, width, height, mouseX, mouseY, linesSupplier); } else if (isHovering(gui, x, y, width, height, mouseX, mouseY)) { List> elements = Lists.newArrayList(); for (Component line : linesSupplier.get()) { elements.add(Either.left(line)); } - elements.add(Either.right(visualComponent)); + elements.addAll(additionalElements); // Just like GuiHelpers#renderTooltip, don't write to the depth buffer, // so that anything that is drawn after this tooltip is not occluded by it. diff --git a/src/main/java/org/cyclops/integratedterminals/core/terminalstorage/slot/TerminalStorageSlotIngredientCraftingOption.java b/src/main/java/org/cyclops/integratedterminals/core/terminalstorage/slot/TerminalStorageSlotIngredientCraftingOption.java index fd243087e..186dc47a2 100644 --- a/src/main/java/org/cyclops/integratedterminals/core/terminalstorage/slot/TerminalStorageSlotIngredientCraftingOption.java +++ b/src/main/java/org/cyclops/integratedterminals/core/terminalstorage/slot/TerminalStorageSlotIngredientCraftingOption.java @@ -1,10 +1,15 @@ package org.cyclops.integratedterminals.core.terminalstorage.slot; +import com.google.common.collect.Lists; +import com.mojang.datafixers.util.Either; import net.minecraft.ChatFormatting; import net.minecraft.client.Minecraft; import net.minecraft.client.gui.GuiGraphics; import net.minecraft.client.gui.screens.inventory.AbstractContainerScreen; import net.minecraft.network.chat.Component; +import net.minecraft.network.chat.FormattedText; +import net.minecraft.world.inventory.tooltip.TooltipComponent; +import net.minecraft.world.item.ItemStack; import net.neoforged.api.distmarker.Dist; import net.neoforged.api.distmarker.OnlyIn; import org.cyclops.commoncapabilities.api.ingredient.IPrototypedIngredient; @@ -16,6 +21,7 @@ import org.cyclops.integratedterminals.client.gui.container.ContainerScreenTerminalStorage; import org.cyclops.integratedterminals.client.gui.container.component.GuiCraftingPlan; import org.cyclops.integratedterminals.client.gui.tooltip.CraftingOptionIngredientsTooltip; +import org.cyclops.integratedterminals.client.gui.tooltip.CraftingOptionMachinesTooltip; import org.cyclops.integratedterminals.client.gui.tooltip.TooltipRenderHelpers; import org.cyclops.integratedterminals.core.terminalstorage.TerminalStorageChannels; import org.cyclops.integratedterminals.core.terminalstorage.TerminalStorageTabIngredientComponentClient; @@ -59,16 +65,16 @@ public void drawGuiContainerLayer(AbstractContainerScreen gui, GuiGraphics guiGr boolean hovering = TooltipRenderHelpers.isHovering(gui, x, y, GuiHelpers.SLOT_SIZE_INNER, GuiHelpers.SLOT_SIZE_INNER, mouseX, mouseY); List>> inputs = hovering ? getInputs() : List.of(); + List machines = hovering ? getCraftingOption().getCraftingOption().getCraftingMachines() : List.of(); viewHandler.drawInstance(guiGraphics, getInstance(), maxQuantity, label, gui, layer, partialTick, x, y, mouseX, mouseY, - hovering ? getTooltipLines(pendingCraftingJobOutput, inputs, tab, channel, label) : null, - inputs.isEmpty() ? null : new CraftingOptionIngredientsTooltip(inputs)); + hovering ? getTooltipLines(pendingCraftingJobOutput, tab, channel, label) : null, + getTooltipElements(machines, inputs)); } drawCraftingJobOverlay(guiGraphics, layer, x, y, pendingCraftingJobOutput); } @OnlyIn(Dist.CLIENT) protected List getTooltipLines(@Nullable PendingCraftingJobOutput pendingCraftingJobOutput, - List>> inputs, ITerminalStorageTabClient tab, int channel, @Nullable String label) { List tooltipLines = createTooltipLines(pendingCraftingJobOutput, tab, channel, label); // An unknown duration says nothing here, so it is left out rather than shown as a placeholder @@ -77,10 +83,6 @@ protected List getTooltipLines(@Nullable PendingCraftingJobOutput tooltipLines.add(Component.translatable("gui.integratedterminals.terminal_storage.tooltip.duration", GuiCraftingPlan.getDurationValue(estimatedTickDuration))); } - if (!inputs.isEmpty()) { - tooltipLines.add(Component.translatable("gui.integratedterminals.terminal_storage.tooltip.requirements") - .withStyle(ChatFormatting.YELLOW)); - } return tooltipLines; } @@ -95,6 +97,36 @@ protected List createChannelTooltipLines(ITerminalStorageTabClient ta : List.of(); } + /** + * Show the inputs that this crafting option requires, and the machines that it is crafted in, + * each as a labelled grid of icons. + * + * @param machines The crafting machines. + * @param inputs The required inputs, with their alternatives. + * @return The tooltip elements. + */ + @OnlyIn(Dist.CLIENT) + protected List> getTooltipElements(List machines, + List>> inputs) { + List> tooltipElements = Lists.newArrayList(); + if (!inputs.isEmpty()) { + tooltipElements.add(Either.left(Component + .translatable("gui.integratedterminals.terminal_storage.tooltip.requirements") + .withStyle(ChatFormatting.YELLOW))); + tooltipElements.add(Either.right(new CraftingOptionIngredientsTooltip(inputs))); + } + if (!machines.isEmpty()) { + // Only a single machine can be named on the label, otherwise the icons have to speak for themselves + tooltipElements.add(Either.left(machines.size() == 1 + ? Component.translatable("gui.integratedterminals.terminal_storage.tooltip.crafting_machine", + machines.get(0).getHoverName()).withStyle(ChatFormatting.YELLOW) + : Component.translatable("gui.integratedterminals.terminal_storage.tooltip.crafting_machines") + .withStyle(ChatFormatting.YELLOW))); + tooltipElements.add(Either.right(new CraftingOptionMachinesTooltip(machines))); + } + return tooltipElements; + } + /** * @return The inputs that are required by this crafting option, with all their alternatives. */ diff --git a/src/main/java/org/cyclops/integratedterminals/gametest/GameTestTerminalCraftingOptionMachines.java b/src/main/java/org/cyclops/integratedterminals/gametest/GameTestTerminalCraftingOptionMachines.java new file mode 100644 index 000000000..be50b14a2 --- /dev/null +++ b/src/main/java/org/cyclops/integratedterminals/gametest/GameTestTerminalCraftingOptionMachines.java @@ -0,0 +1,179 @@ +package org.cyclops.integratedterminals.gametest; + +import com.google.common.collect.Lists; +import com.google.common.collect.Maps; +import com.google.common.collect.Multimap; +import net.minecraft.core.BlockPos; +import net.minecraft.gametest.framework.GameTest; +import net.minecraft.gametest.framework.GameTestHelper; +import net.minecraft.nbt.CompoundTag; +import net.minecraft.resources.ResourceLocation; +import net.minecraft.world.item.Item; +import net.minecraft.world.item.ItemStack; +import net.minecraft.world.item.Items; +import net.minecraft.world.item.crafting.RecipeType; +import net.minecraft.world.level.block.Block; +import net.minecraft.world.level.block.Blocks; +import net.neoforged.neoforge.gametest.GameTestHolder; +import net.neoforged.neoforge.gametest.PrefixGameTestTemplate; +import org.apache.commons.lang3.tuple.Triple; +import org.cyclops.commoncapabilities.api.capability.recipehandler.IRecipeDefinition; +import org.cyclops.commoncapabilities.api.capability.recipehandler.RecipeDefinition; +import org.cyclops.commoncapabilities.api.ingredient.IPrototypedIngredient; +import org.cyclops.commoncapabilities.api.ingredient.IngredientComponent; +import org.cyclops.commoncapabilities.api.ingredient.MixedIngredients; +import org.cyclops.commoncapabilities.api.ingredient.PrototypedIngredient; +import org.cyclops.integratedcrafting.api.crafting.ICraftingInterface; +import org.cyclops.integratedcrafting.core.CraftingHelpers; +import org.cyclops.integratedcrafting.core.part.PartTypeInterfaceCraftingBase; +import org.cyclops.integratedcrafting.gametest.GameTestHelpersIntegratedCrafting; +import org.cyclops.integrateddynamics.api.network.INetwork; +import org.cyclops.integrateddynamics.api.network.IPositionedAddonsNetworkIngredients; +import org.cyclops.integrateddynamics.core.helper.NetworkHelpers; +import org.cyclops.integratedterminals.Reference; +import org.cyclops.integratedterminals.modcompat.integratedcrafting.TerminalCraftingOptionRecipeDefinition; +import org.cyclops.integratedterminals.modcompat.integratedcrafting.TerminalStorageTabIngredientCraftingHandlerCraftingNetwork; + +import java.util.Collection; +import java.util.List; + +/** + * Game tests for the crafting machines that are exposed by crafting options. + * @author rubensworks + */ +@GameTestHolder(Reference.MOD_ID) +@PrefixGameTestTemplate(false) +public class GameTestTerminalCraftingOptionMachines { + + public static final BlockPos POS = BlockPos.ZERO.offset(2, 0, 2); + public static final int TIMEOUT = 2000; + + private static IRecipeDefinition createRecipe() { + List>> inputs = Lists.newArrayList(); + inputs.add(Lists.newArrayList(new PrototypedIngredient<>(IngredientComponent.ITEMSTACK, + new ItemStack(Items.OAK_PLANKS), + IngredientComponent.ITEMSTACK.getMatcher().getExactMatchNoQuantityCondition()))); + return RecipeDefinition.ofIngredients(IngredientComponent.ITEMSTACK, inputs, + MixedIngredients.ofInstance(IngredientComponent.ITEMSTACK, new ItemStack(Items.STICK, 4))); + } + + /** + * The machines of a crafting option must survive being sent to the client. + */ + @GameTest(template = "empty", templateNamespace = "cyclopscore") + public void testCraftingMachinesSerialization(GameTestHelper helper) { + TerminalStorageTabIngredientCraftingHandlerCraftingNetwork handler = + new TerminalStorageTabIngredientCraftingHandlerCraftingNetwork(); + TerminalCraftingOptionRecipeDefinition craftingOption = + new TerminalCraftingOptionRecipeDefinition<>(IngredientComponent.ITEMSTACK, createRecipe(), -1, + Lists.newArrayList(new ItemStack(Items.FURNACE), new ItemStack(Items.CRAFTING_TABLE))); + + CompoundTag tag = handler.serializeCraftingOption(helper.getLevel().registryAccess(), craftingOption); + TerminalCraftingOptionRecipeDefinition deserialized = handler.deserializeCraftingOption( + helper.getLevel().registryAccess(), IngredientComponent.ITEMSTACK, tag); + + List machines = deserialized.getCraftingMachines(); + helper.assertValueEqual(machines.size(), 2, "Crafting machine count is incorrect"); + helper.assertValueEqual(machines.get(0).getItem(), Items.FURNACE, "First crafting machine is incorrect"); + helper.assertValueEqual(machines.get(1).getItem(), Items.CRAFTING_TABLE, "Second crafting machine is incorrect"); + + helper.succeed(); + } + + /** + * A crafting option without machines must not write them, + * so that nothing is sent for handlers that can not determine them. + */ + @GameTest(template = "empty", templateNamespace = "cyclopscore") + public void testCraftingMachinesSerializationEmpty(GameTestHelper helper) { + TerminalStorageTabIngredientCraftingHandlerCraftingNetwork handler = + new TerminalStorageTabIngredientCraftingHandlerCraftingNetwork(); + TerminalCraftingOptionRecipeDefinition craftingOption = + new TerminalCraftingOptionRecipeDefinition<>(IngredientComponent.ITEMSTACK, createRecipe()); + + CompoundTag tag = handler.serializeCraftingOption(helper.getLevel().registryAccess(), craftingOption); + helper.assertFalse(tag.contains("craftingMachines"), "Expected no crafting machines to be serialized"); + + TerminalCraftingOptionRecipeDefinition deserialized = handler.deserializeCraftingOption( + helper.getLevel().registryAccess(), IngredientComponent.ITEMSTACK, tag); + helper.assertTrue(deserialized.getCraftingMachines().isEmpty(), "Expected no crafting machines"); + + helper.succeed(); + } + + /** + * Check the machines that are determined for the first recipe that the network exposes. + * + * @param helper The game test helper. + * @param attuned If an attuned crafting interface should be used. + * @param crafters The machines to place before the crafting interfaces. + * @param expectedInterfaces The expected number of interfaces that expose the recipe. + * @param expectedMachine The expected machine item. + */ + protected void testCraftingMachinesInNetwork(GameTestHelper helper, boolean attuned, Block[] crafters, + int expectedInterfaces, Item expectedMachine, + RecipeType recipeType, ResourceLocation recipeName) { + GameTestHelpersIntegratedCrafting.INetworkPositions> positions = + GameTestHelpersIntegratedCrafting.createBasicNetwork(helper, POS, attuned, crafters); + + // Add the recipe to every crafting interface. + // Attuned interfaces derive their recipes from their machine, so this is a no-op for those. + for (int i = 0; i < crafters.length; i++) { + positions.interfaceRecipeAdders().get(i).accept(Triple.of(0, recipeType, recipeName)); + } + + helper.succeedWhen(() -> { + INetwork network = NetworkHelpers.getNetwork(helper.getLevel(), helper.absolutePos(POS), null) + .orElseThrow(() -> new IllegalStateException("Could not find a network")); + Multimap recipeCraftingInterfaces = CraftingHelpers + .getCraftingNetworkChecked(network) + .getRecipeCraftingInterfaces(IPositionedAddonsNetworkIngredients.DEFAULT_CHANNEL); + helper.assertFalse(recipeCraftingInterfaces.isEmpty(), "The network exposes no recipes yet"); + + IRecipeDefinition recipe = recipeCraftingInterfaces.keySet().iterator().next(); + Collection craftingInterfaces = recipeCraftingInterfaces.get(recipe); + helper.assertValueEqual(craftingInterfaces.size(), expectedInterfaces, + "Crafting interface count is incorrect"); + + List machines = TerminalStorageTabIngredientCraftingHandlerCraftingNetwork + .getCraftingMachines(craftingInterfaces, Maps.newIdentityHashMap()); + helper.assertValueEqual(machines.size(), 1, "Crafting machine count is incorrect"); + helper.assertValueEqual(machines.get(0).getItem(), expectedMachine, "Crafting machine is incorrect"); + }); + } + + @GameTest(template = "empty10", templateNamespace = Reference.MOD_ID, timeoutTicks = TIMEOUT) + public void testCraftingMachinesInNetworkCraftingTable(GameTestHelper helper) { + testCraftingMachinesInNetwork(helper, false, new Block[]{Blocks.CRAFTING_TABLE}, 1, Items.CRAFTING_TABLE, + RecipeType.CRAFTING, ResourceLocation.fromNamespaceAndPath("minecraft", "chest")); + } + + @GameTest(template = "empty10", templateNamespace = Reference.MOD_ID, timeoutTicks = TIMEOUT) + public void testCraftingMachinesInNetworkFurnace(GameTestHelper helper) { + testCraftingMachinesInNetwork(helper, false, new Block[]{Blocks.FURNACE}, 1, Items.FURNACE, + RecipeType.SMELTING, ResourceLocation.fromNamespaceAndPath("minecraft", "iron_ingot_from_smelting_raw_iron")); + } + + @GameTest(template = "empty10", templateNamespace = Reference.MOD_ID, timeoutTicks = TIMEOUT) + public void testCraftingMachinesInNetworkAttunedCraftingTable(GameTestHelper helper) { + testCraftingMachinesInNetwork(helper, true, new Block[]{Blocks.CRAFTING_TABLE}, 1, Items.CRAFTING_TABLE, + RecipeType.CRAFTING, ResourceLocation.fromNamespaceAndPath("minecraft", "chest")); + } + + @GameTest(template = "empty10", templateNamespace = Reference.MOD_ID, timeoutTicks = TIMEOUT) + public void testCraftingMachinesInNetworkAttunedFurnace(GameTestHelper helper) { + testCraftingMachinesInNetwork(helper, true, new Block[]{Blocks.FURNACE}, 1, Items.FURNACE, + RecipeType.SMELTING, ResourceLocation.fromNamespaceAndPath("minecraft", "iron_ingot_from_smelting_raw_iron")); + } + + /** + * Two interfaces that target the same machine type must only show that machine once. + */ + @GameTest(template = "empty10", templateNamespace = Reference.MOD_ID, timeoutTicks = TIMEOUT) + public void testCraftingMachinesInNetworkDeduplicated(GameTestHelper helper) { + testCraftingMachinesInNetwork(helper, false, new Block[]{Blocks.CRAFTING_TABLE, Blocks.CRAFTING_TABLE}, + 2, Items.CRAFTING_TABLE, + RecipeType.CRAFTING, ResourceLocation.fromNamespaceAndPath("minecraft", "chest")); + } + +} diff --git a/src/main/java/org/cyclops/integratedterminals/modcompat/integratedcrafting/TerminalCraftingOptionRecipeDefinition.java b/src/main/java/org/cyclops/integratedterminals/modcompat/integratedcrafting/TerminalCraftingOptionRecipeDefinition.java index 1358ffc2b..1eeb2639c 100644 --- a/src/main/java/org/cyclops/integratedterminals/modcompat/integratedcrafting/TerminalCraftingOptionRecipeDefinition.java +++ b/src/main/java/org/cyclops/integratedterminals/modcompat/integratedcrafting/TerminalCraftingOptionRecipeDefinition.java @@ -2,12 +2,14 @@ import com.google.common.collect.Iterables; import com.google.common.collect.Lists; +import net.minecraft.world.item.ItemStack; import org.cyclops.commoncapabilities.api.capability.recipehandler.IRecipeDefinition; import org.cyclops.commoncapabilities.api.ingredient.IPrototypedIngredient; import org.cyclops.commoncapabilities.api.ingredient.IngredientComponent; import org.cyclops.integratedterminals.api.terminalstorage.crafting.ITerminalCraftingOption; import java.util.Collection; +import java.util.Collections; import java.util.Iterator; import java.util.List; import java.util.stream.Collectors; @@ -23,6 +25,7 @@ public class TerminalCraftingOptionRecipeDefinition implements ITerminalCr private final IngredientComponent ingredientComponent; private final IRecipeDefinition prioritizedRecipe; private final long estimatedTickDuration; + private final List craftingMachines; @Deprecated // TODO: rm in next major public TerminalCraftingOptionRecipeDefinition(IngredientComponent ingredientComponent, @@ -33,9 +36,17 @@ public TerminalCraftingOptionRecipeDefinition(IngredientComponent ingredie public TerminalCraftingOptionRecipeDefinition(IngredientComponent ingredientComponent, IRecipeDefinition prioritizedRecipe, long estimatedTickDuration) { + this(ingredientComponent, prioritizedRecipe, estimatedTickDuration, Collections.emptyList()); + } + + public TerminalCraftingOptionRecipeDefinition(IngredientComponent ingredientComponent, + IRecipeDefinition prioritizedRecipe, + long estimatedTickDuration, + List craftingMachines) { this.ingredientComponent = ingredientComponent; this.prioritizedRecipe = prioritizedRecipe; this.estimatedTickDuration = estimatedTickDuration; + this.craftingMachines = craftingMachines; } @Override @@ -43,6 +54,11 @@ public long getEstimatedTickDuration() { return estimatedTickDuration; } + @Override + public List getCraftingMachines() { + return craftingMachines; + } + @Override public Iterator getOutputs() { return prioritizedRecipe.getOutput().getInstances(ingredientComponent).iterator(); diff --git a/src/main/java/org/cyclops/integratedterminals/modcompat/integratedcrafting/TerminalStorageTabIngredientCraftingHandlerCraftingNetwork.java b/src/main/java/org/cyclops/integratedterminals/modcompat/integratedcrafting/TerminalStorageTabIngredientCraftingHandlerCraftingNetwork.java index fc9e53b90..d2df624c8 100644 --- a/src/main/java/org/cyclops/integratedterminals/modcompat/integratedcrafting/TerminalStorageTabIngredientCraftingHandlerCraftingNetwork.java +++ b/src/main/java/org/cyclops/integratedterminals/modcompat/integratedcrafting/TerminalStorageTabIngredientCraftingHandlerCraftingNetwork.java @@ -1,13 +1,18 @@ package org.cyclops.integratedterminals.modcompat.integratedcrafting; +import com.google.common.collect.ImmutableMultimap; import com.google.common.collect.Lists; +import com.google.common.collect.Maps; +import com.google.common.collect.Multimap; import com.mojang.authlib.GameProfile; import net.minecraft.core.HolderLookup; import net.minecraft.nbt.CompoundTag; import net.minecraft.nbt.IntTag; +import net.minecraft.nbt.ListTag; import net.minecraft.nbt.Tag; import net.minecraft.resources.ResourceLocation; import net.minecraft.server.level.ServerPlayer; +import net.minecraft.world.item.ItemStack; import net.neoforged.neoforge.server.ServerLifecycleHooks; import org.apache.logging.log4j.Level; import org.cyclops.commoncapabilities.api.capability.recipehandler.IRecipeDefinition; @@ -71,25 +76,64 @@ public int[] getChannels(TerminalStorageTabIngredientComponentServer ingredientComponent = tab.getIngredientNetwork().getComponent(); IRecipeIndex recipeIndex = getRecipeIndex(tab.getNetwork(), channel); ICraftingNetwork craftingNetwork = CraftingHelpers.getCraftingNetwork(tab.getNetwork()).orElse(null); + Multimap recipeCraftingInterfaces = craftingNetwork == null + ? ImmutableMultimap.of() : craftingNetwork.getRecipeCraftingInterfaces(channel); + // Multiple recipes are commonly exposed by the same crafting interface, + // so only resolve the machine of each interface once. + Map machineCache = Maps.newIdentityHashMap(); Iterable recipes = () -> recipeIndex.getRecipes(ingredientComponent, instance, matchCondition); return StreamSupport.stream(recipes.spliterator(), false) .map((recipe) -> new TerminalCraftingOptionRecipeDefinition<>(ingredientComponent, recipe, - craftingNetwork == null ? -1 : craftingNetwork.getEstimatedRecipeDuration(channel, recipe))) + craftingNetwork == null ? -1 : craftingNetwork.getEstimatedRecipeDuration(channel, recipe), + getCraftingMachines(recipeCraftingInterfaces.get(recipe), machineCache))) .collect(Collectors.toList()); } + /** + * Determine the distinct machines that are targeted by the given crafting interfaces. + * @param craftingInterfaces The crafting interfaces that expose a recipe. + * @param machineCache A cache of machines by crafting interface. + * @return The machines, without duplicates. + */ + public static List getCraftingMachines(Collection craftingInterfaces, + Map machineCache) { + List craftingMachines = Lists.newArrayList(); + for (ICraftingInterface craftingInterface : craftingInterfaces) { + ItemStack machine = machineCache.computeIfAbsent(craftingInterface, ICraftingInterface::getTargetMachineItem); + // Different interfaces can target the same machine type, which we only want to show once + if (!machine.isEmpty() && craftingMachines.stream() + .noneMatch(existing -> ItemStack.isSameItemSameComponents(existing, machine))) { + craftingMachines.add(machine); + } + } + return craftingMachines; + } + @Override public CompoundTag serializeCraftingOption(HolderLookup.Provider lookupProvider, TerminalCraftingOptionRecipeDefinition craftingOption) { CompoundTag tag = IRecipeDefinition.serialize(lookupProvider, craftingOption.getRecipe()); tag.putLong("estimatedTickDuration", craftingOption.getEstimatedTickDuration()); + List craftingMachines = craftingOption.getCraftingMachines(); + if (!craftingMachines.isEmpty()) { + ListTag craftingMachinesTag = new ListTag(); + for (ItemStack craftingMachine : craftingMachines) { + craftingMachinesTag.add(craftingMachine.save(lookupProvider)); + } + tag.put("craftingMachines", craftingMachinesTag); + } return tag; } @Override public TerminalCraftingOptionRecipeDefinition deserializeCraftingOption(HolderLookup.Provider lookupProvider, IngredientComponent ingredientComponent, CompoundTag tag) throws IllegalArgumentException { + List craftingMachines = Lists.newArrayList(); + for (Tag craftingMachineTag : tag.getList("craftingMachines", Tag.TAG_COMPOUND)) { + ItemStack.parse(lookupProvider, craftingMachineTag).ifPresent(craftingMachines::add); + } return new TerminalCraftingOptionRecipeDefinition<>(ingredientComponent, IRecipeDefinition.deserialize(lookupProvider, tag), - tag.contains("estimatedTickDuration", Tag.TAG_LONG) ? tag.getLong("estimatedTickDuration") : -1); + tag.contains("estimatedTickDuration", Tag.TAG_LONG) ? tag.getLong("estimatedTickDuration") : -1, + craftingMachines); } @Override diff --git a/src/main/java/org/cyclops/integratedterminals/proxy/ClientProxy.java b/src/main/java/org/cyclops/integratedterminals/proxy/ClientProxy.java index 7c68e9286..4a1e3e959 100644 --- a/src/main/java/org/cyclops/integratedterminals/proxy/ClientProxy.java +++ b/src/main/java/org/cyclops/integratedterminals/proxy/ClientProxy.java @@ -16,7 +16,9 @@ import org.cyclops.integratedterminals.IntegratedTerminals; import org.cyclops.integratedterminals.Reference; import org.cyclops.integratedterminals.client.gui.tooltip.ClientCraftingOptionIngredientsTooltip; +import org.cyclops.integratedterminals.client.gui.tooltip.ClientCraftingOptionMachinesTooltip; import org.cyclops.integratedterminals.client.gui.tooltip.CraftingOptionIngredientsTooltip; +import org.cyclops.integratedterminals.client.gui.tooltip.CraftingOptionMachinesTooltip; import org.cyclops.integratedterminals.item.ItemTerminalStoragePortable; import org.cyclops.integratedterminals.network.packet.TerminalStorageIngredientItemOpenGenericPacket; import org.lwjgl.glfw.GLFW; @@ -69,6 +71,7 @@ public ModBase getMod() { public void registerClientTooltipComponentFactories(RegisterClientTooltipComponentFactoriesEvent event) { event.register(CraftingOptionIngredientsTooltip.class, ClientCraftingOptionIngredientsTooltip::new); + event.register(CraftingOptionMachinesTooltip.class, ClientCraftingOptionMachinesTooltip::new); } @Override diff --git a/src/main/resources/assets/integratedterminals/lang/en_us.json b/src/main/resources/assets/integratedterminals/lang/en_us.json index f4e21826a..35bab79db 100644 --- a/src/main/resources/assets/integratedterminals/lang/en_us.json +++ b/src/main/resources/assets/integratedterminals/lang/en_us.json @@ -18,6 +18,8 @@ "gui.integratedterminals.terminal_storage.no_tabs": "No tabs available. Connect storage to this network with an interface.", "gui.integratedterminals.terminal_storage.ender_chest": "Ender Storage", "gui.integratedterminals.terminal_storage.craft": "craft", + "gui.integratedterminals.terminal_storage.tooltip.crafting_machine": "Crafted In: %s", + "gui.integratedterminals.terminal_storage.tooltip.crafting_machines": "Crafted In:", "gui.integratedterminals.terminal_storage.tooltip.requirements": "Crafting Requirements:", "gui.integratedterminals.terminal_storage.tooltip.crafting": "Being crafted: %s", "gui.integratedterminals.terminal_storage.tooltip.duration": "Estimated: %s per craft",