diff --git a/src/generated/resources/assets/gtceu/lang/en_ud.json b/src/generated/resources/assets/gtceu/lang/en_ud.json
index a0ddc38c98..0768d0203f 100644
--- a/src/generated/resources/assets/gtceu/lang/en_ud.json
+++ b/src/generated/resources/assets/gtceu/lang/en_ud.json
@@ -3711,6 +3711,10 @@
"gtceu.multiblock.ore_rig.drilled_ore_entry": "%s - ",
"gtceu.multiblock.ore_rig.drilled_ores_list": ":sǝɹO",
"gtceu.multiblock.ore_rig.ore_amount": "%s :ǝʇɐᴚ buıןןıɹᗡ",
+ "gtceu.multiblock.output_line.0": ")ɐǝ/%ss( ɹ§%sǝ§ x %s",
+ "gtceu.multiblock.output_line.1": ")s/%s( ɹ§%sǝ§ x %s",
+ "gtceu.multiblock.output_line.2": ")ɐǝ/%ss( ɹ§%sǝ§ ≈ %s",
+ "gtceu.multiblock.output_line.3": ")s/%s( ɹ§%sǝ§ ≈ %s",
"gtceu.multiblock.page_switcher.io.both": "sʇndʇnO + sʇnduI pǝuıqɯoƆϛ§",
"gtceu.multiblock.page_switcher.io.export": "sʇndʇnOㄣ§",
"gtceu.multiblock.page_switcher.io.import": "sʇnduIᄅ§",
@@ -3767,7 +3771,7 @@
"gtceu.multiblock.primitive_water_pump.extra2.3": "xㄣ :ɥɔʇɐH ʇndʇnO ΛꞀ ",
"gtceu.multiblock.primitive_water_pump.extra2.4": "",
"gtceu.multiblock.primitive_water_pump.extra2.5": "˙%%0ϛ ʎq pǝsɐǝɹɔuı ǝq ןןıʍ uoıʇɔnpoɹd ɹǝʇɐʍ ןɐʇoʇ ǝɥʇ 'ǝɯoıᗺ s,dɯnԀ ǝɥʇ uı buıuıɐɹ ǝןıɥM",
- "gtceu.multiblock.progress": "%s%% :ssǝɹboɹԀ",
+ "gtceu.multiblock.progress": ")%s%%( %ss / %ss :ssǝɹboɹԀ",
"gtceu.multiblock.pyrolyse_oven.description": "˙ןıO ʎʌɐǝH puɐ ɥsⱯ ɹo 'ןıO ǝʇosoǝɹƆ puɐ ןɐoɔɹɐɥƆ oʇuı sboꞀ buıuɹnʇ ɹoɟ pǝsn ǝɹnʇɔnɹʇs ʞɔoןqıʇןnɯ ɐ sı uǝʌO ǝsʎןoɹʎԀ ǝɥ⟘",
"gtceu.multiblock.pyrolyse_oven.speed": "%s%% :pǝǝdS buıssǝɔoɹԀ",
"gtceu.multiblock.require_steam_parts": "¡sǝsnᗺ puɐ sǝɥɔʇɐH ɯɐǝʇS sǝɹınbǝᴚ",
diff --git a/src/generated/resources/assets/gtceu/lang/en_us.json b/src/generated/resources/assets/gtceu/lang/en_us.json
index a885320d34..cb22554531 100644
--- a/src/generated/resources/assets/gtceu/lang/en_us.json
+++ b/src/generated/resources/assets/gtceu/lang/en_us.json
@@ -3711,6 +3711,10 @@
"gtceu.multiblock.ore_rig.drilled_ore_entry": " - %s",
"gtceu.multiblock.ore_rig.drilled_ores_list": "Ores:",
"gtceu.multiblock.ore_rig.ore_amount": "Drilling Rate: %s",
+ "gtceu.multiblock.output_line.0": "%s x §e%s§r (%ss/ea)",
+ "gtceu.multiblock.output_line.1": "%s x §e%s§r (%s/s)",
+ "gtceu.multiblock.output_line.2": "%s ≈ §e%s§r (%ss/ea)",
+ "gtceu.multiblock.output_line.3": "%s ≈ §e%s§r (%s/s)",
"gtceu.multiblock.page_switcher.io.both": "§5Combined Inputs + Outputs",
"gtceu.multiblock.page_switcher.io.export": "§4Outputs",
"gtceu.multiblock.page_switcher.io.import": "§2Inputs",
@@ -3767,7 +3771,7 @@
"gtceu.multiblock.primitive_water_pump.extra2.3": " LV Output Hatch: 4x",
"gtceu.multiblock.primitive_water_pump.extra2.4": "",
"gtceu.multiblock.primitive_water_pump.extra2.5": "While raining in the Pump's Biome, the total water production will be increased by 50%%.",
- "gtceu.multiblock.progress": "Progress: %s%%",
+ "gtceu.multiblock.progress": "Progress: %ss / %ss (%s%%)",
"gtceu.multiblock.pyrolyse_oven.description": "The Pyrolyse Oven is a multiblock structure used for turning Logs into Charcoal and Creosote Oil, or Ash and Heavy Oil.",
"gtceu.multiblock.pyrolyse_oven.speed": "Processing Speed: %s%%",
"gtceu.multiblock.require_steam_parts": "Requires Steam Hatches and Buses!",
diff --git a/src/main/java/com/gregtechceu/gtceu/api/machine/feature/multiblock/IDisplayUIMachine.java b/src/main/java/com/gregtechceu/gtceu/api/machine/feature/multiblock/IDisplayUIMachine.java
index 4ea74e60c9..365106eca2 100644
--- a/src/main/java/com/gregtechceu/gtceu/api/machine/feature/multiblock/IDisplayUIMachine.java
+++ b/src/main/java/com/gregtechceu/gtceu/api/machine/feature/multiblock/IDisplayUIMachine.java
@@ -42,7 +42,7 @@ default ModularUI createUI(Player entityPlayer) {
screen.addWidget(new ComponentPanelWidget(4, 17, this::addDisplayText)
.textSupplier(this.self().getLevel().isClientSide ? null : this::addDisplayText)
.setMaxWidthLimit(150)
- .clickHandler(this::handleDisplayClick));
+ .clickHandler(this::handleDisplayClick)).setSizeHeight(8);
return new ModularUI(176, 216, this, entityPlayer)
.background(GuiTextures.BACKGROUND)
.widget(screen)
diff --git a/src/main/java/com/gregtechceu/gtceu/api/machine/multiblock/MultiblockDisplayText.java b/src/main/java/com/gregtechceu/gtceu/api/machine/multiblock/MultiblockDisplayText.java
index d229164d60..cd2637cb83 100644
--- a/src/main/java/com/gregtechceu/gtceu/api/machine/multiblock/MultiblockDisplayText.java
+++ b/src/main/java/com/gregtechceu/gtceu/api/machine/multiblock/MultiblockDisplayText.java
@@ -2,7 +2,11 @@
import com.gregtechceu.gtceu.api.GTValues;
import com.gregtechceu.gtceu.api.capability.IEnergyContainer;
+import com.gregtechceu.gtceu.api.capability.recipe.FluidRecipeCapability;
+import com.gregtechceu.gtceu.api.capability.recipe.ItemRecipeCapability;
+import com.gregtechceu.gtceu.api.recipe.GTRecipe;
import com.gregtechceu.gtceu.api.recipe.GTRecipeType;
+import com.gregtechceu.gtceu.api.recipe.RecipeHelper;
import com.gregtechceu.gtceu.config.ConfigHolder;
import com.gregtechceu.gtceu.utils.FormattingUtil;
import com.gregtechceu.gtceu.utils.GTUtil;
@@ -299,27 +303,100 @@ public Builder addIdlingLine(boolean checkState) {
}
/**
- * Adds a simple progress line that displays progress as a percentage.
+ * Adds a simple progress line that displays the current time of a recipe and its progress as a percentage.
*
* Added if structure is formed and the machine is active.
*
+ * @param currentDuration The current duration of the recipe in ticks
+ * @param maxDuration The max duration of the recipe in ticks
* @param progressPercent Progress formatted as a range of [0,1] representing the progress of the recipe.
*/
- public Builder addProgressLine(double progressPercent) { // todo
+ public Builder addProgressLine(double currentDuration, double maxDuration, double progressPercent) {
if (!isStructureFormed || !isActive)
return this;
int currentProgress = (int) (progressPercent * 100);
- textList.add(Component.translatable("gtceu.multiblock.progress", currentProgress));
+ double currentInSec = currentDuration / 20.0;
+ double maxInSec = maxDuration / 20.0;
+ textList.add(Component.translatable("gtceu.multiblock.progress",
+ String.format("%.2f", (float) currentInSec),
+ String.format("%.2f", (float) maxInSec), currentProgress));
+ return this;
+ }
+
+ public Builder addOutputLines(GTRecipe recipe, int chanceTier) {
+ if (!isStructureFormed || !isActive)
+ return this;
+ if (recipe != null) {
+ var function = recipe.getType().getChanceFunction();
+ double maxDurationSec = (double) recipe.duration / 20.0;
+ var itemOutputs = recipe.getOutputContents(ItemRecipeCapability.CAP);
+ var fluidOutputs = recipe.getOutputContents(FluidRecipeCapability.CAP);
+
+ for (var item : itemOutputs) {
+ var stack = (ItemRecipeCapability.CAP.of(item.content).getItems()[0]);
+ if (stack.getCount() < maxDurationSec) {
+ if (item.chance < item.maxChance) {
+ double averageDurationforRoll = (double) item.maxChance / (double) function
+ .getBoostedChance(item, RecipeHelper.getPreOCRecipeEuTier(recipe), chanceTier);
+ textList.add(Component.translatable("gtceu.multiblock.output_line.2", stack.getHoverName(),
+ stack.getCount(),
+ FormattingUtil.formatNumber2Places(averageDurationforRoll * maxDurationSec)));
+ } else {
+ textList.add(Component.translatable("gtceu.multiblock.output_line.0", stack.getHoverName(),
+ stack.getCount(), maxDurationSec));
+ }
+ } else {
+ double countPerSec = (double) stack.getCount() / maxDurationSec;
+ if (item.chance < item.maxChance) {
+ double averageDurationforRoll = (double) item.maxChance / (double) function
+ .getBoostedChance(item, RecipeHelper.getPreOCRecipeEuTier(recipe), chanceTier);
+ textList.add(Component.translatable("gtceu.multiblock.output_line.3",
+ stack.getHoverName(), stack.getCount(),
+ FormattingUtil.formatNumber2Places(averageDurationforRoll * countPerSec)));
+ } else {
+ textList.add(Component.translatable("gtceu.multiblock.output_line.1",
+ stack.getHoverName(), stack.getCount(), countPerSec));
+ }
+ }
+ }
+ for (var fluid : fluidOutputs) {
+ var stack = (FluidRecipeCapability.CAP.of(fluid.content).getStacks()[0]);
+ if (stack.getAmount() < maxDurationSec) {
+ if (fluid.chance < fluid.maxChance) {
+ double averageDurationforRoll = (double) fluid.maxChance / (double) function
+ .getBoostedChance(fluid, RecipeHelper.getPreOCRecipeEuTier(recipe), chanceTier);
+ textList.add(Component.translatable("gtceu.multiblock.output_line.2",
+ stack.getDisplayName(), stack.getAmount(),
+ FormattingUtil.formatNumber2Places(averageDurationforRoll * maxDurationSec)));
+ } else {
+ textList.add(Component.translatable("gtceu.multiblock.output_line.0",
+ stack.getDisplayName(), stack.getAmount(),
+ FormattingUtil.formatNumber2Places(maxDurationSec)));
+ }
+ } else {
+ double countPerSec = (double) stack.getAmount() / maxDurationSec;
+ if (fluid.chance < fluid.maxChance) {
+ double averageDurationforRoll = (double) fluid.maxChance / (double) function
+ .getBoostedChance(fluid, RecipeHelper.getPreOCRecipeEuTier(recipe), chanceTier);
+ textList.add(Component.translatable("gtceu.multiblock.output_line.3",
+ stack.getDisplayName(), stack.getAmount(),
+ FormattingUtil.formatNumber2Places(averageDurationforRoll * countPerSec)));
+ } else {
+ textList.add(Component.translatable("gtceu.multiblock.output_line.1",
+ stack.getDisplayName(), stack.getAmount(),
+ FormattingUtil.formatNumber2Places(countPerSec)));
+ }
+ }
+ }
+ }
return this;
}
/**
- * Adds a line indicating how many parallels this multi can potentially perform.
- *
- * Added if structure is formed and the number of parallels is greater than one.
+ * Adds a line indicating the current mode of the multi
*/
- public Builder addMachineModeLine(GTRecipeType recipeType) {
- if (!isStructureFormed)
+ public Builder addMachineModeLine(GTRecipeType recipeType, boolean hasMultipleModes) {
+ if (!isStructureFormed || !hasMultipleModes)
return this;
textList.add(Component
.translatable("gtceu.gui.machinemode",
diff --git a/src/main/java/com/gregtechceu/gtceu/api/machine/multiblock/WorkableElectricMultiblockMachine.java b/src/main/java/com/gregtechceu/gtceu/api/machine/multiblock/WorkableElectricMultiblockMachine.java
index e518d25895..3462a2b0b7 100644
--- a/src/main/java/com/gregtechceu/gtceu/api/machine/multiblock/WorkableElectricMultiblockMachine.java
+++ b/src/main/java/com/gregtechceu/gtceu/api/machine/multiblock/WorkableElectricMultiblockMachine.java
@@ -95,10 +95,12 @@ public void addDisplayText(List textList) {
.setWorkingStatus(recipeLogic.isWorkingEnabled(), recipeLogic.isActive())
.addEnergyUsageLine(energyContainer)
.addEnergyTierLine(tier)
- .addMachineModeLine(getRecipeType())
+ .addMachineModeLine(getRecipeType(), getRecipeTypes().length > 1)
.addParallelsLine(numParallels)
.addWorkingStatusLine()
- .addProgressLine(recipeLogic.getProgressPercent());
+ .addProgressLine(recipeLogic.getProgress(), recipeLogic.getMaxProgress(),
+ recipeLogic.getProgressPercent())
+ .addOutputLines(recipeLogic.getLastRecipe(), this.getChanceTier());
getDefinition().getAdditionalDisplay().accept(this, textList);
IDisplayUIMachine.super.addDisplayText(textList);
}
@@ -110,7 +112,7 @@ public Widget createUIWidget() {
.addWidget(new LabelWidget(4, 5, self().getBlockState().getBlock().getDescriptionId()))
.addWidget(new ComponentPanelWidget(4, 17, this::addDisplayText)
.textSupplier(this.getLevel().isClientSide ? null : this::addDisplayText)
- .setMaxWidthLimit(150)
+ .setMaxWidthLimit(200)
.clickHandler(this::handleDisplayClick)));
group.setBackground(GuiTextures.BACKGROUND_INVERSE);
return group;
diff --git a/src/main/java/com/gregtechceu/gtceu/data/lang/LangHandler.java b/src/main/java/com/gregtechceu/gtceu/data/lang/LangHandler.java
index 3350d6555c..84a39d84d5 100644
--- a/src/main/java/com/gregtechceu/gtceu/data/lang/LangHandler.java
+++ b/src/main/java/com/gregtechceu/gtceu/data/lang/LangHandler.java
@@ -1067,7 +1067,11 @@ public static void init(RegistrateLangProvider provider) {
provider.add("gtceu.multiblock.not_enough_energy", "WARNING: Machine needs more energy.");
provider.add("gtceu.multiblock.not_enough_energy_output", "WARNING: Energy Dynamo Tier Too Low!");
provider.add("gtceu.multiblock.waiting", "WARNING: Machine is waiting.");
- provider.add("gtceu.multiblock.progress", "Progress: %s%%");
+ provider.add("gtceu.multiblock.progress", "Progress: %ss / %ss (%s%%)");
+ provider.add("gtceu.multiblock.output_line.0", "%s x §e%s§r (%ss/ea)");
+ provider.add("gtceu.multiblock.output_line.1", "%s x §e%s§r (%s/s)");
+ provider.add("gtceu.multiblock.output_line.2", "%s ≈ §e%s§r (%ss/ea)");
+ provider.add("gtceu.multiblock.output_line.3", "%s ≈ §e%s§r (%s/s)");
provider.add("gtceu.multiblock.invalid_structure", "Invalid structure.");
provider.add("gtceu.multiblock.invalid_structure.tooltip",
"This block is a controller of the multiblock structure. For building help, see structure template in JEI.");
diff --git a/src/main/java/com/gregtechceu/gtceu/utils/FormattingUtil.java b/src/main/java/com/gregtechceu/gtceu/utils/FormattingUtil.java
index 95d3974d75..3d4320f6d7 100644
--- a/src/main/java/com/gregtechceu/gtceu/utils/FormattingUtil.java
+++ b/src/main/java/com/gregtechceu/gtceu/utils/FormattingUtil.java
@@ -179,6 +179,11 @@ public static String formatNumber2Places(float number) {
return DECIMAL_FORMAT_2F.format(number);
}
+ @NotNull
+ public static String formatNumber2Places(double number) {
+ return DECIMAL_FORMAT_2F.format(number);
+ }
+
public static Component formatPercentage2Places(String langKey, float percentage) {
return Component.translatable(langKey, formatNumber2Places(percentage)).withStyle(YELLOW);
}