diff --git a/src/main/java/gregtech/api/capability/impl/AbstractRecipeLogic.java b/src/main/java/gregtech/api/capability/impl/AbstractRecipeLogic.java
index 99132df53dd..90c43f781a0 100644
--- a/src/main/java/gregtech/api/capability/impl/AbstractRecipeLogic.java
+++ b/src/main/java/gregtech/api/capability/impl/AbstractRecipeLogic.java
@@ -187,6 +187,12 @@ protected IMultipleTankHandler getOutputTank() {
return metaTileEntity.getExportFluids();
}
+ public Recipe getParallelRecipe() {
+ if (previousRecipe == null) return null;
+ return findParallelRecipe(previousRecipe, getInputInventory(), getInputTank(),
+ getOutputInventory(), getOutputTank(), getMaxParallelVoltage(), getParallelLimit());
+ }
+
/**
* @return true if energy is consumed by this Recipe Logic, otherwise false
*/
diff --git a/src/main/java/gregtech/api/metatileentity/multiblock/MultiMapMultiblockController.java b/src/main/java/gregtech/api/metatileentity/multiblock/MultiMapMultiblockController.java
index 1fccce2d897..1e581113c0d 100644
--- a/src/main/java/gregtech/api/metatileentity/multiblock/MultiMapMultiblockController.java
+++ b/src/main/java/gregtech/api/metatileentity/multiblock/MultiMapMultiblockController.java
@@ -166,7 +166,8 @@ protected void configureDisplayText(MultiblockUIBuilder builder) {
.addEnergyTierLine(GTUtility.getTierByVoltage(recipeMapWorkable.getMaxVoltage()))
.addParallelsLine(recipeMapWorkable.getParallelLimit())
.addWorkingStatusLine()
- .addProgressLine(recipeMapWorkable.getProgressPercent());
+ .addProgressLine(recipeMapWorkable.getProgress(), recipeMapWorkable.getMaxProgress())
+ .addRecipeOutputLine(recipeMapWorkable);
}
@Override
diff --git a/src/main/java/gregtech/api/metatileentity/multiblock/RecipeMapMultiblockController.java b/src/main/java/gregtech/api/metatileentity/multiblock/RecipeMapMultiblockController.java
index 9ce6514702b..7d241836797 100644
--- a/src/main/java/gregtech/api/metatileentity/multiblock/RecipeMapMultiblockController.java
+++ b/src/main/java/gregtech/api/metatileentity/multiblock/RecipeMapMultiblockController.java
@@ -151,7 +151,8 @@ protected void configureDisplayText(MultiblockUIBuilder builder) {
.addEnergyTierLine(GTUtility.getTierByVoltage(recipeMapWorkable.getMaxVoltage()))
.addParallelsLine(recipeMapWorkable.getParallelLimit())
.addWorkingStatusLine()
- .addProgressLine(recipeMapWorkable.getProgressPercent());
+ .addProgressLine(recipeMapWorkable.getProgress(), recipeMapWorkable.getMaxProgress())
+ .addRecipeOutputLine(recipeMapWorkable);
}
protected void configureWarningText(MultiblockUIBuilder builder) {
diff --git a/src/main/java/gregtech/api/metatileentity/multiblock/RecipeMapSteamMultiblockController.java b/src/main/java/gregtech/api/metatileentity/multiblock/RecipeMapSteamMultiblockController.java
index 0ccda35523e..53cf2b3f0ac 100644
--- a/src/main/java/gregtech/api/metatileentity/multiblock/RecipeMapSteamMultiblockController.java
+++ b/src/main/java/gregtech/api/metatileentity/multiblock/RecipeMapSteamMultiblockController.java
@@ -118,7 +118,8 @@ protected void configureDisplayText(MultiblockUIBuilder builder) {
})
.addParallelsLine(recipeMapWorkable.getParallelLimit())
.addWorkingStatusLine()
- .addProgressLine(recipeMapWorkable.getProgressPercent());
+ .addProgressLine(recipeMapWorkable.getProgress(), recipeMapWorkable.getMaxProgress())
+ .addRecipeOutputLine(recipeMapWorkable);
}
@Override
diff --git a/src/main/java/gregtech/api/metatileentity/multiblock/ui/MultiblockUIBuilder.java b/src/main/java/gregtech/api/metatileentity/multiblock/ui/MultiblockUIBuilder.java
index 08304c4db60..73903a160f8 100644
--- a/src/main/java/gregtech/api/metatileentity/multiblock/ui/MultiblockUIBuilder.java
+++ b/src/main/java/gregtech/api/metatileentity/multiblock/ui/MultiblockUIBuilder.java
@@ -2,23 +2,38 @@
import gregtech.api.GTValues;
import gregtech.api.capability.IEnergyContainer;
+import gregtech.api.capability.impl.AbstractRecipeLogic;
+import gregtech.api.mui.GTByteBufAdapters;
+import gregtech.api.mui.drawable.GTObjectDrawable;
+import gregtech.api.recipes.Recipe;
import gregtech.api.recipes.RecipeMap;
+import gregtech.api.recipes.chance.boost.ChanceBoostFunction;
+import gregtech.api.recipes.chance.output.impl.ChancedFluidOutput;
+import gregtech.api.recipes.chance.output.impl.ChancedItemOutput;
+import gregtech.api.util.FluidStackHashStrategy;
+import gregtech.api.util.GTUtility;
+import gregtech.api.util.ItemStackHashStrategy;
import gregtech.api.util.KeyUtil;
import gregtech.api.util.TextFormattingUtil;
import gregtech.common.ConfigHolder;
+import net.minecraft.item.ItemStack;
import net.minecraft.network.PacketBuffer;
import net.minecraft.util.text.TextFormatting;
+import net.minecraftforge.fluids.FluidStack;
import com.cleanroommc.modularui.api.drawable.IDrawable;
import com.cleanroommc.modularui.api.drawable.IKey;
import com.cleanroommc.modularui.api.drawable.IRichTextBuilder;
+import com.cleanroommc.modularui.utils.serialization.ByteBufAdapters;
import com.cleanroommc.modularui.utils.serialization.IByteBufDeserializer;
import com.cleanroommc.modularui.utils.serialization.IByteBufSerializer;
import com.cleanroommc.modularui.value.sync.PanelSyncManager;
import com.cleanroommc.modularui.value.sync.SyncHandler;
import io.netty.buffer.ByteBuf;
import io.netty.buffer.Unpooled;
+import it.unimi.dsi.fastutil.objects.Object2LongLinkedOpenCustomHashMap;
+import it.unimi.dsi.fastutil.objects.Object2LongMap;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
@@ -46,6 +61,8 @@ public class MultiblockUIBuilder {
private final InternalSyncHandler syncHandler = new InternalSyncHandler();
private final KeyManager manager = new InternalKeyManager();
+ private static final int DEFAULT_MAX_RECIPE_LINES = 25;
+
@Nullable
private InternalSyncer syncer;
@@ -313,17 +330,23 @@ public MultiblockUIBuilder addIdlingLine(boolean checkState) {
}
/**
- * Adds a simple progress line that displays progress as a percentage.
+ * Adds a progress line that displays recipe progress as "time / total time (percentage)".
*
* Added if structure is formed and the machine is active.
*
- * @param progressPercent Progress formatted as a range of [0,1] representing the progress of the recipe.
+ * @param progress current progress.
+ * @param maxProgress total progress to be made.
*/
- public MultiblockUIBuilder addProgressLine(double progressPercent) {
+ public MultiblockUIBuilder addProgressLine(int progress, int maxProgress) {
if (!isStructureFormed || !isActive) return this;
- addKey(KeyUtil.lang(TextFormatting.GRAY,
- "gregtech.multiblock.progress",
- (int) (getSyncer().syncDouble(progressPercent) * 100)));
+
+ progress = getSyncer().syncInt(progress);
+ maxProgress = getSyncer().syncInt(maxProgress);
+
+ addKey(KeyUtil.lang(TextFormatting.WHITE, "gregtech.multiblock.recipe_progress",
+ String.format("%,3.2f", (float) progress / 20),
+ String.format("%,3.2f", (float) maxProgress / 20),
+ String.format("%,3.1f", (float) progress / maxProgress * 100f)));
return this;
}
@@ -484,6 +507,201 @@ public MultiblockUIBuilder addRecipeMapLine(RecipeMap> map) {
return this;
}
+ /**
+ * Adds the current outputs of a recipe from recipe logic. Items then fluids.
+ *
+ * @param arl an instance of an {@link AbstractRecipeLogic} to gather the outputs from.
+ */
+ public MultiblockUIBuilder addRecipeOutputLine(@NotNull AbstractRecipeLogic arl) {
+ return addRecipeOutputLine(arl, DEFAULT_MAX_RECIPE_LINES);
+ }
+
+ /**
+ * Adds the current outputs of a recipe from recipe logic. Items then fluids.
+ *
+ * @param arl an instance of an {@link AbstractRecipeLogic} to gather the outputs from.
+ * @param maxLines the maximum number of lines to print until truncating with {@code ...}
+ */
+ public MultiblockUIBuilder addRecipeOutputLine(AbstractRecipeLogic arl, int maxLines) {
+ // todo doing this every tick on the server is probably not good
+ Recipe recipe = arl.getParallelRecipe();
+
+ if (getSyncer().syncBoolean(recipe == null)) return this;
+ if (getSyncer().syncBoolean(arl.getRecipeMap() == null)) return this;
+
+ // noinspection DataFlowIssue
+ long eut = getSyncer().syncLong(() -> recipe.getEUt());
+ long maxVoltage = getSyncer().syncLong(arl.getMaximumOverclockVoltage());
+
+ List itemOutputs = new ArrayList<>();
+ List chancedItemOutputs = new ArrayList<>();
+ List fluidOutputs = new ArrayList<>();
+ List chancedFluidOutputs = new ArrayList<>();
+
+ if (isServer()) {
+ // recipe is checked indirectly for null
+ // noinspection DataFlowIssue
+ itemOutputs.addAll(recipe.getOutputs());
+ chancedItemOutputs.addAll(recipe.getChancedOutputs().getChancedEntries());
+ fluidOutputs.addAll(recipe.getFluidOutputs());
+ chancedFluidOutputs.addAll(recipe.getChancedFluidOutputs().getChancedEntries());
+ }
+
+ itemOutputs = getSyncer().syncCollection(itemOutputs, ByteBufAdapters.ITEM_STACK);
+ fluidOutputs = getSyncer().syncCollection(fluidOutputs, ByteBufAdapters.FLUID_STACK);
+ chancedItemOutputs = getSyncer().syncCollection(chancedItemOutputs, GTByteBufAdapters.CHANCED_ITEM_OUTPUT);
+ chancedFluidOutputs = getSyncer().syncCollection(chancedFluidOutputs, GTByteBufAdapters.CHANCED_FLUID_OUTPUT);
+
+ addKey(KeyUtil.string(TextFormatting.GRAY, "Producing: "), Operation.NEW_LINE);
+
+ var chanceFunction = arl.getRecipeMap().getChanceFunction();
+ int recipeTier = GTUtility.getTierByVoltage(eut);
+ int machineTier = GTUtility.getOCTierByVoltage(maxVoltage);
+
+ // items
+
+ Object2LongMap itemMap = new Object2LongLinkedOpenCustomHashMap<>(
+ ItemStackHashStrategy.comparingAllButCount());
+
+ for (ItemStack stack : itemOutputs) {
+ itemMap.merge(stack, (long) stack.getCount(), Long::sum);
+ }
+
+ for (var stack : itemMap.keySet()) {
+ addItemOutputLine(stack, itemMap.getLong(stack), arl.getMaxProgress());
+ }
+
+ for (var chancedItemOutput : chancedItemOutputs) {
+ addChancedItemOutputLine(chancedItemOutput, chanceFunction, recipeTier, machineTier, arl.getMaxProgress());
+ }
+
+ // fluids
+
+ Object2LongMap fluidMap = new Object2LongLinkedOpenCustomHashMap<>(
+ FluidStackHashStrategy.comparingAllButAmount);
+
+ for (FluidStack stack : fluidOutputs) {
+ fluidMap.merge(stack, (long) stack.amount, Long::sum);
+ }
+
+ for (var stack : fluidMap.keySet()) {
+ addFluidOutputLine(stack, fluidMap.getLong(stack), arl.getMaxProgress());
+ }
+
+ for (var chancedFluidOutput : chancedFluidOutputs) {
+ addChancedFluidOutputLine(chancedFluidOutput, chanceFunction, recipeTier, machineTier,
+ arl.getMaxProgress());
+ }
+ return this;
+ }
+
+ /**
+ * Add an item output of a recipe to the display.
+ *
+ * @param stack the {@link ItemStack} to display.
+ * @param recipeLength the recipe length, in ticks.
+ */
+ private void addItemOutputLine(@NotNull ItemStack stack, long count, int recipeLength) {
+ IKey name = KeyUtil.string(TextFormatting.AQUA, stack.getDisplayName());
+ IKey amount = KeyUtil.number(TextFormatting.GOLD, count);
+ IKey rate = KeyUtil.string(TextFormatting.WHITE,
+ formatRecipeRate(getSyncer().syncInt(recipeLength), count));
+
+ addKey(new GTObjectDrawable(stack, count)
+ .asIcon()
+ .asHoverable()
+ .addTooltipLine(formatRecipeData(name, amount, rate)), Operation.ADD);
+ // addKey(IKey.SPACE, Operation.ADD);
+ }
+
+ /**
+ * Add the fluid outputs of a recipe to the display.
+ *
+ * @param stack a {@link FluidStack}s to display.
+ * @param recipeLength the recipe length, in ticks.
+ */
+ private void addFluidOutputLine(@NotNull FluidStack stack, long count, int recipeLength) {
+ IKey name = KeyUtil.fluid(TextFormatting.AQUA, stack);
+ IKey amount = KeyUtil.number(TextFormatting.GOLD, count);
+ IKey rate = KeyUtil.string(TextFormatting.WHITE,
+ formatRecipeRate(getSyncer().syncInt(recipeLength), count));
+
+ addKey(new GTObjectDrawable(stack, count)
+ .asIcon()
+ .asHoverable()
+ .addTooltipLine(formatRecipeData(name, amount, rate)), Operation.ADD);
+ // addKey(IKey.SPACE, Operation.ADD);
+ }
+
+ /**
+ * Add a chanced item output of a recipe to the display.
+ *
+ * @param chancedItemOutput chanced item output
+ * @param boostFunction function to calculate boosted chance
+ * @param recipeTier voltage tier of the recipe
+ * @param machineTier machine tier
+ * @param recipeLength max duration of the recipe
+ */
+ private void addChancedItemOutputLine(@NotNull ChancedItemOutput chancedItemOutput,
+ ChanceBoostFunction boostFunction,
+ int recipeTier, int machineTier, int recipeLength) {
+ var stack = chancedItemOutput.getIngredient();
+ IKey name = KeyUtil.string(TextFormatting.AQUA, stack.getDisplayName());
+ IKey amount = KeyUtil.number(TextFormatting.GOLD, stack.getCount());
+ IKey rate = KeyUtil.string(TextFormatting.WHITE,
+ formatRecipeRate(getSyncer().syncInt(recipeLength), stack.getCount()));
+
+ addKey(new GTObjectDrawable(chancedItemOutput, stack.getCount())
+ .setBoostFunction(entry -> boostFunction.getBoostedChance(entry, recipeTier, machineTier))
+ .asIcon()
+ .asHoverable()
+ .addTooltipLine(formatRecipeData(name, amount, rate)), Operation.ADD);
+ // addKey(IKey.SPACE, Operation.ADD);
+ }
+
+ /**
+ * Add a chanced fluid output of a recipe to the display.
+ *
+ * @param chancedFluidOutput chanced fluid output
+ * @param boostFunction function to calculate boosted chance
+ * @param recipeTier voltage tier of the recipe
+ * @param machineTier machine tier
+ * @param recipeLength max duration of the recipe
+ */
+ private void addChancedFluidOutputLine(@NotNull ChancedFluidOutput chancedFluidOutput,
+ ChanceBoostFunction boostFunction,
+ int recipeTier, int machineTier, int recipeLength) {
+ var stack = chancedFluidOutput.getIngredient();
+ IKey name = KeyUtil.fluid(TextFormatting.AQUA, stack);
+ IKey amount = KeyUtil.number(TextFormatting.GOLD, stack.amount);
+ IKey rate = KeyUtil.string(TextFormatting.WHITE,
+ formatRecipeRate(getSyncer().syncInt(recipeLength), stack.amount));
+
+ addKey(new GTObjectDrawable(chancedFluidOutput, stack.amount)
+ .setBoostFunction(entry -> boostFunction.getBoostedChance(entry, recipeTier, machineTier))
+ .asIcon()
+ .asHoverable()
+ .addTooltipLine(formatRecipeData(name, amount, rate)), Operation.ADD);
+ // addKey(IKey.SPACE, Operation.ADD);
+ }
+
+ private static String formatRecipeRate(int recipeLength, long amount) {
+ float perSecond = ((float) amount / recipeLength) * 20f;
+
+ String rate;
+ if (perSecond > 1) {
+ rate = "(" + String.format("%,.2f", perSecond).replaceAll("\\.?0+$", "") + "/s)";
+ } else {
+ rate = "(" + String.format("%,.2f", 1 / (perSecond)).replaceAll("\\.?0+$", "") + "s/ea)";
+ }
+
+ return rate;
+ }
+
+ private static IKey formatRecipeData(IKey name, IKey amount, IKey rate) {
+ return IKey.comp(name, KeyUtil.string(TextFormatting.WHITE, " x "), amount, IKey.SPACE, rate);
+ }
+
/** Insert an empty line into the text list. */
public MultiblockUIBuilder addEmptyLine() {
addKey(IKey.LINE_FEED);
diff --git a/src/main/java/gregtech/api/metatileentity/multiblock/ui/UISyncer.java b/src/main/java/gregtech/api/metatileentity/multiblock/ui/UISyncer.java
index cc96b789138..58cb0eee1bf 100644
--- a/src/main/java/gregtech/api/metatileentity/multiblock/ui/UISyncer.java
+++ b/src/main/java/gregtech/api/metatileentity/multiblock/ui/UISyncer.java
@@ -1,10 +1,11 @@
package gregtech.api.metatileentity.multiblock.ui;
+import gregtech.api.mui.GTByteBufAdapters;
+
import com.cleanroommc.modularui.utils.serialization.ByteBufAdapters;
import com.cleanroommc.modularui.utils.serialization.IByteBufAdapter;
import com.cleanroommc.modularui.utils.serialization.IByteBufDeserializer;
import com.cleanroommc.modularui.utils.serialization.IByteBufSerializer;
-import com.cleanroommc.modularui.utils.serialization.IEquals;
import org.jetbrains.annotations.NotNull;
import java.math.BigInteger;
@@ -16,11 +17,6 @@
public interface UISyncer {
- IByteBufAdapter BIG_INT = ByteBufAdapters.makeAdapter(
- buffer -> new BigInteger(buffer.readByteArray()),
- (buffer, value) -> buffer.writeByteArray(value.toByteArray()),
- IEquals.defaultTester());
-
/**
* Calls the supplier server side only so there's no potential NPEs for the client
*
@@ -98,7 +94,7 @@ default float syncFloat(float initial) {
}
default BigInteger syncBigInt(BigInteger initial) {
- return syncObject(initial, BIG_INT);
+ return syncObject(initial, GTByteBufAdapters.BIG_INT);
}
T syncObject(T initial, IByteBufSerializer serializer, IByteBufDeserializer deserializer);
diff --git a/src/main/java/gregtech/api/mui/GTByteBufAdapters.java b/src/main/java/gregtech/api/mui/GTByteBufAdapters.java
new file mode 100644
index 00000000000..86757aae225
--- /dev/null
+++ b/src/main/java/gregtech/api/mui/GTByteBufAdapters.java
@@ -0,0 +1,54 @@
+package gregtech.api.mui;
+
+import gregtech.api.recipes.chance.output.impl.ChancedFluidOutput;
+import gregtech.api.recipes.chance.output.impl.ChancedItemOutput;
+
+import net.minecraft.network.PacketBuffer;
+
+import com.cleanroommc.modularui.utils.serialization.*;
+import org.jetbrains.annotations.NotNull;
+import org.jetbrains.annotations.Nullable;
+
+import java.io.IOException;
+import java.math.BigInteger;
+
+public class GTByteBufAdapters {
+
+ public static final IByteBufAdapter CHANCED_ITEM_OUTPUT = makeAdapter(
+ ChancedItemOutput::fromBuffer, ChancedItemOutput::toBuffer);
+
+ public static final IByteBufAdapter CHANCED_FLUID_OUTPUT = makeAdapter(
+ ChancedFluidOutput::fromBuffer, ChancedFluidOutput::toBuffer);
+
+ public static final IByteBufAdapter BIG_INT = makeAdapter(
+ buffer -> new BigInteger(buffer.readByteArray()),
+ (buffer, value) -> buffer.writeByteArray(value.toByteArray()));
+
+ public static IByteBufAdapter makeAdapter(@NotNull IByteBufDeserializer deserializer,
+ @NotNull IByteBufSerializer serializer) {
+ return makeAdapter(deserializer, serializer, IEquals.defaultTester());
+ }
+
+ public static IByteBufAdapter makeAdapter(@NotNull IByteBufDeserializer deserializer,
+ @NotNull IByteBufSerializer serializer,
+ @Nullable IEquals equals) {
+ final IEquals tester = equals != null ? equals : IEquals.defaultTester();
+ return new IByteBufAdapter<>() {
+
+ @Override
+ public T deserialize(PacketBuffer buffer) throws IOException {
+ return deserializer.deserialize(buffer);
+ }
+
+ @Override
+ public void serialize(PacketBuffer buffer, T u) throws IOException {
+ serializer.serialize(buffer, u);
+ }
+
+ @Override
+ public boolean areEqual(@NotNull T t1, @NotNull T t2) {
+ return tester.areEqual(t1, t2);
+ }
+ };
+ }
+}
diff --git a/src/main/java/gregtech/api/mui/drawable/GTObjectDrawable.java b/src/main/java/gregtech/api/mui/drawable/GTObjectDrawable.java
new file mode 100644
index 00000000000..775c1b2cd24
--- /dev/null
+++ b/src/main/java/gregtech/api/mui/drawable/GTObjectDrawable.java
@@ -0,0 +1,91 @@
+package gregtech.api.mui.drawable;
+
+import gregtech.api.mui.GTGuiTextures;
+import gregtech.api.recipes.chance.boost.BoostableChanceEntry;
+
+import net.minecraft.item.ItemStack;
+import net.minecraftforge.fluids.FluidStack;
+
+import com.cleanroommc.modularui.api.drawable.IDrawable;
+import com.cleanroommc.modularui.drawable.GuiDraw;
+import com.cleanroommc.modularui.drawable.Icon;
+import com.cleanroommc.modularui.drawable.text.TextRenderer;
+import com.cleanroommc.modularui.screen.viewport.GuiContext;
+import com.cleanroommc.modularui.theme.WidgetTheme;
+import com.cleanroommc.modularui.utils.Alignment;
+import com.cleanroommc.modularui.utils.Color;
+import com.cleanroommc.modularui.utils.NumberFormat;
+import com.cleanroommc.modularui.widget.Widget;
+
+import java.util.function.Function;
+
+public class GTObjectDrawable implements IDrawable {
+
+ private static final TextRenderer renderer = new TextRenderer();
+
+ private final Object object;
+ private final long amount;
+ private Function, Integer> boostFunction;
+
+ public GTObjectDrawable(Object object, long amount) {
+ this.object = object;
+ this.amount = amount;
+ }
+
+ public GTObjectDrawable setBoostFunction(Function, Integer> boostFunction) {
+ this.boostFunction = boostFunction;
+ return this;
+ }
+
+ static {
+ renderer.setScale(0.5f);
+ renderer.setShadow(true);
+ renderer.setColor(Color.WHITE.main);
+ }
+
+ @Override
+ public void draw(GuiContext context, int x, int y, int width, int height, WidgetTheme widgetTheme) {
+ renderer.setAlignment(Alignment.BottomRight, width - 1, height - 1);
+ drawObject(object, context, x, y, width, height, widgetTheme);
+ if (amount > 1) {
+ renderer.setPos(x + 1, y + 1);
+ String amount = NumberFormat.formatWithMaxDigits(this.amount, 3);
+ if (object instanceof FluidStack) amount += "L";
+ renderer.draw(amount);
+ }
+ }
+
+ private void drawObject(Object object, GuiContext context, int x, int y, int width, int height,
+ WidgetTheme widgetTheme) {
+ if (object instanceof ItemStack stack) {
+ GTGuiTextures.SLOT.draw(context, x, y, width, height, widgetTheme);
+ GuiDraw.drawItem(stack, x + 1, y + 1, width - 2, height - 2);
+ } else if (object instanceof FluidStack stack) {
+ GTGuiTextures.FLUID_SLOT.draw(context, x, y, width, height, widgetTheme);
+ GuiDraw.drawFluidTexture(stack, x + 1, y + 1, width - 2, height - 2, 0);
+ } else if (object instanceof BoostableChanceEntry>entry) {
+ drawObject(entry.getIngredient(), context, x, y, width, height, widgetTheme);
+ String chance = "~" + this.boostFunction.apply(entry) / 100 + "%";
+ if (amount > 1) y -= 4;
+ renderer.setPos(x + 1, y + 1);
+ renderer.draw(chance);
+ }
+ }
+
+ @Override
+ public Icon asIcon() {
+ return IDrawable.super.asIcon().size(18);
+ }
+
+ @Override
+ public Widget> asWidget() {
+ return IDrawable.super.asWidget().size(18);
+ }
+
+ public Object getObject() {
+ if (object instanceof BoostableChanceEntry>entry) {
+ return entry.getIngredient();
+ }
+ return object;
+ }
+}
diff --git a/src/main/java/gregtech/api/recipes/chance/output/impl/ChancedFluidOutput.java b/src/main/java/gregtech/api/recipes/chance/output/impl/ChancedFluidOutput.java
index 81a145fe1c8..5d59e6cebfa 100644
--- a/src/main/java/gregtech/api/recipes/chance/output/impl/ChancedFluidOutput.java
+++ b/src/main/java/gregtech/api/recipes/chance/output/impl/ChancedFluidOutput.java
@@ -2,10 +2,14 @@
import gregtech.api.recipes.chance.output.BoostableChanceOutput;
+import net.minecraft.network.PacketBuffer;
import net.minecraftforge.fluids.FluidStack;
+import com.cleanroommc.modularui.network.NetworkUtils;
import org.jetbrains.annotations.NotNull;
+import java.util.Objects;
+
/**
* Implementation for a chanced fluid output
*/
@@ -29,4 +33,15 @@ public String toString() {
", chanceBoost=" + getChanceBoost() +
'}';
}
+
+ public static ChancedFluidOutput fromBuffer(PacketBuffer buffer) {
+ return new ChancedFluidOutput(Objects.requireNonNull(NetworkUtils.readFluidStack(buffer)), buffer.readVarInt(),
+ buffer.readVarInt());
+ }
+
+ public static void toBuffer(PacketBuffer buffer, ChancedFluidOutput value) {
+ NetworkUtils.writeFluidStack(buffer, value.getIngredient());
+ buffer.writeVarInt(value.getChance());
+ buffer.writeVarInt(value.getChanceBoost());
+ }
}
diff --git a/src/main/java/gregtech/api/recipes/chance/output/impl/ChancedItemOutput.java b/src/main/java/gregtech/api/recipes/chance/output/impl/ChancedItemOutput.java
index b49e640d942..a186a01b392 100644
--- a/src/main/java/gregtech/api/recipes/chance/output/impl/ChancedItemOutput.java
+++ b/src/main/java/gregtech/api/recipes/chance/output/impl/ChancedItemOutput.java
@@ -4,7 +4,9 @@
import gregtech.api.util.GTStringUtils;
import net.minecraft.item.ItemStack;
+import net.minecraft.network.PacketBuffer;
+import com.cleanroommc.modularui.network.NetworkUtils;
import org.jetbrains.annotations.NotNull;
/**
@@ -29,4 +31,14 @@ public String toString() {
", chanceBoost=" + getChanceBoost() +
'}';
}
+
+ public static ChancedItemOutput fromBuffer(PacketBuffer buffer) {
+ return new ChancedItemOutput(NetworkUtils.readItemStack(buffer), buffer.readVarInt(), buffer.readVarInt());
+ }
+
+ public static void toBuffer(PacketBuffer buffer, ChancedItemOutput value) {
+ NetworkUtils.writeItemStack(buffer, value.getIngredient());
+ buffer.writeVarInt(value.getChance());
+ buffer.writeVarInt(value.getChanceBoost());
+ }
}
diff --git a/src/main/java/gregtech/api/util/FluidStackHashStrategy.java b/src/main/java/gregtech/api/util/FluidStackHashStrategy.java
new file mode 100644
index 00000000000..8aacb2d39d5
--- /dev/null
+++ b/src/main/java/gregtech/api/util/FluidStackHashStrategy.java
@@ -0,0 +1,68 @@
+package gregtech.api.util;
+
+import net.minecraftforge.fluids.FluidStack;
+
+import it.unimi.dsi.fastutil.Hash;
+
+import java.util.Objects;
+
+public interface FluidStackHashStrategy extends Hash.Strategy {
+
+ static FluidStackHashStrategyBuilder builder() {
+ return new FluidStackHashStrategyBuilder();
+ }
+
+ FluidStackHashStrategy comparingAll = builder()
+ .compareFluid()
+ .compareAmount()
+ .compareNBT()
+ .build();
+
+ FluidStackHashStrategy comparingAllButAmount = builder()
+ .compareFluid()
+ .compareNBT()
+ .build();
+
+ class FluidStackHashStrategyBuilder {
+
+ private boolean fluid, amount, nbt = false;
+
+ public FluidStackHashStrategyBuilder compareFluid() {
+ this.fluid = true;
+ return this;
+ }
+
+ public FluidStackHashStrategyBuilder compareAmount() {
+ this.amount = true;
+ return this;
+ }
+
+ public FluidStackHashStrategyBuilder compareNBT() {
+ this.nbt = true;
+ return this;
+ }
+
+ public FluidStackHashStrategy build() {
+ return new FluidStackHashStrategy() {
+
+ @Override
+ public int hashCode(FluidStack other) {
+ return other == null ? 0 : Objects.hash(
+ fluid ? other.getFluid() : null,
+ amount ? other.amount : null,
+ nbt ? other.tag : null);
+ }
+
+ @Override
+ public boolean equals(FluidStack a, FluidStack b) {
+ if (a == null) return b == null;
+ if (b == null) return false;
+
+ return (!fluid || a.getFluid() == b.getFluid()) &&
+ (!amount || a.amount == b.amount) &&
+ (!nbt || Objects.equals(a.tag, b.tag));
+ }
+ };
+ }
+ }
+}
diff --git a/src/main/java/gregtech/common/metatileentities/multi/electric/MetaTileEntityCleanroom.java b/src/main/java/gregtech/common/metatileentities/multi/electric/MetaTileEntityCleanroom.java
index 0b1edad8de7..f4584488be9 100644
--- a/src/main/java/gregtech/common/metatileentities/multi/electric/MetaTileEntityCleanroom.java
+++ b/src/main/java/gregtech/common/metatileentities/multi/electric/MetaTileEntityCleanroom.java
@@ -504,7 +504,7 @@ protected void configureDisplayText(MultiblockUIBuilder builder) {
cleanState));
}
})
- .addProgressLine(getProgressPercent() / 100.0)
+ .addProgressLine(getProgress(), getMaxProgress())
.addWorkingStatusLine();
}
diff --git a/src/main/java/gregtech/common/metatileentities/multi/electric/MetaTileEntityCrackingUnit.java b/src/main/java/gregtech/common/metatileentities/multi/electric/MetaTileEntityCrackingUnit.java
index 62d05fd2aa2..a20c11176c1 100644
--- a/src/main/java/gregtech/common/metatileentities/multi/electric/MetaTileEntityCrackingUnit.java
+++ b/src/main/java/gregtech/common/metatileentities/multi/electric/MetaTileEntityCrackingUnit.java
@@ -102,7 +102,8 @@ protected void configureDisplayText(MultiblockUIBuilder builder) {
})
.addParallelsLine(recipeMapWorkable.getParallelLimit())
.addWorkingStatusLine()
- .addProgressLine(recipeMapWorkable.getProgressPercent());
+ .addProgressLine(recipeMapWorkable.getProgress(), recipeMapWorkable.getMaxProgress())
+ .addRecipeOutputLine(recipeMapWorkable);
}
@Override
diff --git a/src/main/java/gregtech/common/metatileentities/multi/electric/MetaTileEntityElectricBlastFurnace.java b/src/main/java/gregtech/common/metatileentities/multi/electric/MetaTileEntityElectricBlastFurnace.java
index d57007262aa..b70680943f2 100644
--- a/src/main/java/gregtech/common/metatileentities/multi/electric/MetaTileEntityElectricBlastFurnace.java
+++ b/src/main/java/gregtech/common/metatileentities/multi/electric/MetaTileEntityElectricBlastFurnace.java
@@ -78,7 +78,8 @@ protected void configureDisplayText(MultiblockUIBuilder builder) {
.addCustom(this::addHeatCapacity)
.addParallelsLine(recipeMapWorkable.getParallelLimit())
.addWorkingStatusLine()
- .addProgressLine(recipeMapWorkable.getProgressPercent());
+ .addProgressLine(recipeMapWorkable.getProgress(), recipeMapWorkable.getMaxProgress())
+ .addRecipeOutputLine(recipeMapWorkable);
}
private void addHeatCapacity(KeyManager keyManager, UISyncer syncer) {
diff --git a/src/main/java/gregtech/common/metatileentities/multi/electric/MetaTileEntityFluidDrill.java b/src/main/java/gregtech/common/metatileentities/multi/electric/MetaTileEntityFluidDrill.java
index b0ac1aa59ec..d6ce9f408e1 100644
--- a/src/main/java/gregtech/common/metatileentities/multi/electric/MetaTileEntityFluidDrill.java
+++ b/src/main/java/gregtech/common/metatileentities/multi/electric/MetaTileEntityFluidDrill.java
@@ -195,7 +195,7 @@ protected void configureDisplayText(MultiblockUIBuilder builder) {
}
}
})
- .addProgressLine(minerLogic.getProgressPercent())
+ .addProgressLine(minerLogic.getProgressTime(), FluidDrillLogic.MAX_PROGRESS)
.addWorkingStatusLine();
}
diff --git a/src/main/java/gregtech/common/metatileentities/multi/electric/MetaTileEntityMultiSmelter.java b/src/main/java/gregtech/common/metatileentities/multi/electric/MetaTileEntityMultiSmelter.java
index 41ebaeb15a6..382a54719b5 100644
--- a/src/main/java/gregtech/common/metatileentities/multi/electric/MetaTileEntityMultiSmelter.java
+++ b/src/main/java/gregtech/common/metatileentities/multi/electric/MetaTileEntityMultiSmelter.java
@@ -91,7 +91,8 @@ protected void configureDisplayText(MultiblockUIBuilder builder) {
}
})
.addWorkingStatusLine()
- .addProgressLine(recipeMapWorkable.getProgressPercent());
+ .addProgressLine(recipeMapWorkable.getProgress(), recipeMapWorkable.getMaxProgress())
+ .addRecipeOutputLine(recipeMapWorkable);
}
@Override
diff --git a/src/main/java/gregtech/common/metatileentities/multi/electric/MetaTileEntityProcessingArray.java b/src/main/java/gregtech/common/metatileentities/multi/electric/MetaTileEntityProcessingArray.java
index 7355e0c91eb..fbb5d7faeab 100644
--- a/src/main/java/gregtech/common/metatileentities/multi/electric/MetaTileEntityProcessingArray.java
+++ b/src/main/java/gregtech/common/metatileentities/multi/electric/MetaTileEntityProcessingArray.java
@@ -170,7 +170,8 @@ protected void configureDisplayText(MultiblockUIBuilder builder) {
})
.addParallelsLine(recipeMapWorkable.getParallelLimit())
.addWorkingStatusLine()
- .addProgressLine(recipeMapWorkable.getProgressPercent());
+ .addProgressLine(recipeMapWorkable.getProgress(), recipeMapWorkable.getMaxProgress())
+ .addRecipeOutputLine(recipeMapWorkable);
}
@SideOnly(Side.CLIENT)
diff --git a/src/main/java/gregtech/common/metatileentities/multi/electric/MetaTileEntityPyrolyseOven.java b/src/main/java/gregtech/common/metatileentities/multi/electric/MetaTileEntityPyrolyseOven.java
index 8734ee35a67..6213867dd68 100644
--- a/src/main/java/gregtech/common/metatileentities/multi/electric/MetaTileEntityPyrolyseOven.java
+++ b/src/main/java/gregtech/common/metatileentities/multi/electric/MetaTileEntityPyrolyseOven.java
@@ -128,7 +128,8 @@ protected void configureDisplayText(MultiblockUIBuilder builder) {
})
.addParallelsLine(recipeMapWorkable.getParallelLimit())
.addWorkingStatusLine()
- .addProgressLine(recipeMapWorkable.getProgressPercent());
+ .addProgressLine(recipeMapWorkable.getProgress(), recipeMapWorkable.getMaxProgress())
+ .addRecipeOutputLine(recipeMapWorkable);
}
@Override
diff --git a/src/main/java/gregtech/common/metatileentities/multi/electric/MetaTileEntityResearchStation.java b/src/main/java/gregtech/common/metatileentities/multi/electric/MetaTileEntityResearchStation.java
index db325ef9524..5e2bed97a52 100644
--- a/src/main/java/gregtech/common/metatileentities/multi/electric/MetaTileEntityResearchStation.java
+++ b/src/main/java/gregtech/common/metatileentities/multi/electric/MetaTileEntityResearchStation.java
@@ -233,7 +233,7 @@ protected void configureDisplayText(MultiblockUIBuilder builder) {
.addComputationUsageExactLine(getRecipeMapWorkable().getCurrentDrawnCWUt())
.addParallelsLine(recipeMapWorkable.getParallelLimit())
.addWorkingStatusLine()
- .addProgressLine(recipeMapWorkable.getProgressPercent());
+ .addProgressLine(recipeMapWorkable.getProgress(), recipeMapWorkable.getMaxProgress());
}
@Override
diff --git a/src/main/java/gregtech/common/mui/widget/ScrollableTextWidget.java b/src/main/java/gregtech/common/mui/widget/ScrollableTextWidget.java
index 55e82d4f2b0..12ee6435302 100644
--- a/src/main/java/gregtech/common/mui/widget/ScrollableTextWidget.java
+++ b/src/main/java/gregtech/common/mui/widget/ScrollableTextWidget.java
@@ -1,5 +1,8 @@
package gregtech.common.mui.widget;
+import gregtech.api.mui.drawable.GTObjectDrawable;
+import gregtech.mixins.mui2.IconAccessor;
+
import com.cleanroommc.modularui.api.GuiAxis;
import com.cleanroommc.modularui.api.drawable.IHoverable;
import com.cleanroommc.modularui.api.drawable.IRichTextBuilder;
@@ -7,9 +10,11 @@
import com.cleanroommc.modularui.api.layout.IViewportStack;
import com.cleanroommc.modularui.api.widget.IGuiAction;
import com.cleanroommc.modularui.api.widget.Interactable;
+import com.cleanroommc.modularui.drawable.DelegateIcon;
import com.cleanroommc.modularui.drawable.Stencil;
import com.cleanroommc.modularui.drawable.text.RichText;
import com.cleanroommc.modularui.drawable.text.TextRenderer;
+import com.cleanroommc.modularui.integration.jei.JeiIngredientProvider;
import com.cleanroommc.modularui.screen.ModularScreen;
import com.cleanroommc.modularui.screen.RichTooltip;
import com.cleanroommc.modularui.screen.viewport.ModularGuiContext;
@@ -24,12 +29,14 @@
import java.util.function.Consumer;
public class ScrollableTextWidget extends Widget
- implements IRichTextBuilder, Interactable, IViewport {
+ implements IRichTextBuilder, Interactable, IViewport,
+ JeiIngredientProvider {
private final RichText text = new RichText();
private Consumer builder;
private boolean dirty = false;
private boolean autoUpdate = false;
+ private Object lastIngredient;
private final ScrollArea scroll = new ScrollArea();
private final TextRenderer renderer = new TextRenderer();
@@ -66,6 +73,13 @@ public void drawForeground(ModularGuiContext context) {
tooltip.draw(context);
}
}
+ if (getHoveredElement() instanceof DelegateIcon delegateIcon &&
+ delegateIcon.findRootDelegate() instanceof IconAccessor accessor &&
+ accessor.getDrawable() instanceof GTObjectDrawable objectDrawable) {
+ lastIngredient = objectDrawable.getObject();
+ } else {
+ lastIngredient = null;
+ }
}
@Override
@@ -207,4 +221,9 @@ public ScrollableTextWidget textBuilder(Consumer builder) {
markDirty();
return this;
}
+
+ @Override
+ public @Nullable Object getIngredient() {
+ return this.lastIngredient;
+ }
}
diff --git a/src/main/java/gregtech/mixins/mui2/IconAccessor.java b/src/main/java/gregtech/mixins/mui2/IconAccessor.java
new file mode 100644
index 00000000000..59e2ba009e4
--- /dev/null
+++ b/src/main/java/gregtech/mixins/mui2/IconAccessor.java
@@ -0,0 +1,13 @@
+package gregtech.mixins.mui2;
+
+import com.cleanroommc.modularui.api.drawable.IDrawable;
+import com.cleanroommc.modularui.drawable.Icon;
+import org.spongepowered.asm.mixin.Mixin;
+import org.spongepowered.asm.mixin.gen.Accessor;
+
+@Mixin(value = Icon.class, remap = false)
+public interface IconAccessor {
+
+ @Accessor()
+ IDrawable getDrawable();
+}
diff --git a/src/main/java/gregtech/mixins/mui2/RichTextCompilerMixin.java b/src/main/java/gregtech/mixins/mui2/RichTextCompilerMixin.java
new file mode 100644
index 00000000000..79c44c7d20b
--- /dev/null
+++ b/src/main/java/gregtech/mixins/mui2/RichTextCompilerMixin.java
@@ -0,0 +1,41 @@
+package gregtech.mixins.mui2;
+
+import net.minecraft.client.gui.FontRenderer;
+
+import com.cleanroommc.modularui.api.drawable.IKey;
+import com.cleanroommc.modularui.drawable.text.RichTextCompiler;
+import com.llamalad7.mixinextras.sugar.Local;
+import org.spongepowered.asm.mixin.Mixin;
+import org.spongepowered.asm.mixin.Shadow;
+import org.spongepowered.asm.mixin.injection.At;
+import org.spongepowered.asm.mixin.injection.Inject;
+import org.spongepowered.asm.mixin.injection.ModifyArg;
+import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
+
+import java.util.List;
+
+@Mixin(value = RichTextCompiler.class, remap = false)
+public class RichTextCompilerMixin {
+
+ @Shadow
+ private int x;
+
+ @Shadow
+ private FontRenderer fr;
+
+ @ModifyArg(method = "trimRight",
+ at = @At(value = "INVOKE",
+ target = "Ljava/lang/String;substring(II)Ljava/lang/String;"),
+ index = 1)
+ private static int fixTrim(int beginIndex) {
+ return beginIndex + 1;
+ }
+
+ @Inject(method = "compile",
+ at = @At(value = "INVOKE",
+ target = "Lcom/cleanroommc/modularui/drawable/text/RichTextCompiler;addLineElement(Ljava/lang/Object;)V",
+ ordinal = 0))
+ private void moveXString(List