diff --git a/src/main/java/gregtech/api/capability/GregtechDataCodes.java b/src/main/java/gregtech/api/capability/GregtechDataCodes.java index b2e7077cc59..ae106662690 100644 --- a/src/main/java/gregtech/api/capability/GregtechDataCodes.java +++ b/src/main/java/gregtech/api/capability/GregtechDataCodes.java @@ -99,6 +99,7 @@ public static int assignId() { public static final int UPDATE_UPWARDS_FACING = assignId(); public static final int UPDATE_FLIP = assignId(); public static final int LOCK_FILL = assignId(); + public static final int MUFFLER_OBSTRUCTED = assignId(); // Item Bus Item Stack Auto Collapsing public static final int TOGGLE_COLLAPSE_ITEMS = assignId(); diff --git a/src/main/java/gregtech/api/capability/IHPCAComponentHatch.java b/src/main/java/gregtech/api/capability/IHPCAComponentHatch.java index fbd9915ebdb..1fc10b8a441 100644 --- a/src/main/java/gregtech/api/capability/IHPCAComponentHatch.java +++ b/src/main/java/gregtech/api/capability/IHPCAComponentHatch.java @@ -1,6 +1,9 @@ package gregtech.api.capability; -import gregtech.api.gui.resources.TextureArea; +import gregtech.api.metatileentity.MetaTileEntity; +import gregtech.api.mui.GTGuiTextures; + +import com.cleanroommc.modularui.drawable.UITexture; public interface IHPCAComponentHatch { @@ -46,5 +49,17 @@ default void setDamaged(boolean damaged) {} /** * The icon for this component in the HPCA's UI. Should be a 13x13 px sprite. */ - TextureArea getComponentIcon(); + default UITexture getComponentIcon() { + return GTGuiTextures.HPCA_ICON_EMPTY_COMPONENT; + } + + /** + * The untranslated name of the tile implementing an HPCA component + */ + default String getTileName() { + if (this instanceof MetaTileEntity mte) { + return mte.getMetaFullName(); + } + return ""; + } } diff --git a/src/main/java/gregtech/api/capability/impl/AbstractRecipeLogic.java b/src/main/java/gregtech/api/capability/impl/AbstractRecipeLogic.java index d86d2827139..e92b3b13295 100644 --- a/src/main/java/gregtech/api/capability/impl/AbstractRecipeLogic.java +++ b/src/main/java/gregtech/api/capability/impl/AbstractRecipeLogic.java @@ -43,6 +43,7 @@ import org.jetbrains.annotations.Nullable; import java.util.ArrayList; +import java.util.Collections; import java.util.List; import static gregtech.api.GTValues.ULV; @@ -70,8 +71,10 @@ public abstract class AbstractRecipeLogic extends MTETrait implements IWorkable, protected int progressTime; protected int maxProgressTime; protected long recipeEUt; - protected List fluidOutputs; - protected List itemOutputs; + @NotNull + protected List fluidOutputs = Collections.emptyList(); + @NotNull + protected List itemOutputs = Collections.emptyList(); protected boolean isActive; protected boolean workingEnabled = true; @@ -338,6 +341,10 @@ public void setParallelRecipesPerformed(int amount) { this.parallelRecipesPerformed = amount; } + public int getParallelRecipesPerformed() { + return parallelRecipesPerformed; + } + /** * Update the current running recipe's progress *

@@ -974,8 +981,8 @@ protected void completeRecipe() { this.progressTime = 0; setMaxProgress(0); this.recipeEUt = 0; - this.fluidOutputs = null; - this.itemOutputs = null; + this.fluidOutputs = Collections.emptyList(); + this.itemOutputs = Collections.emptyList(); this.hasNotEnoughEnergy = false; this.wasActiveAndNeedsUpdate = true; this.parallelRecipesPerformed = 0; @@ -1135,8 +1142,8 @@ public void invalidate() { progressTime = 0; maxProgressTime = 0; recipeEUt = 0; - fluidOutputs = null; - itemOutputs = null; + fluidOutputs = Collections.emptyList(); + itemOutputs = Collections.emptyList(); parallelRecipesPerformed = 0; isOutputsFull = false; invalidInputsForRecipes = false; @@ -1211,7 +1218,7 @@ public void deserializeNBT(@NotNull NBTTagCompound compound) { this.itemOutputs.add(new ItemStack(itemOutputsList.getCompoundTagAt(i))); } NBTTagList fluidOutputsList = compound.getTagList("FluidOutputs", Constants.NBT.TAG_COMPOUND); - this.fluidOutputs = new ArrayList<>(); + this.fluidOutputs = new ArrayList<>(fluidOutputsList.tagCount()); for (int i = 0; i < fluidOutputsList.tagCount(); i++) { this.fluidOutputs.add(FluidStack.loadFluidStackFromNBT(fluidOutputsList.getCompoundTagAt(i))); } diff --git a/src/main/java/gregtech/api/capability/impl/BoilerRecipeLogic.java b/src/main/java/gregtech/api/capability/impl/BoilerRecipeLogic.java index a2163db4637..5624ba6eab7 100644 --- a/src/main/java/gregtech/api/capability/impl/BoilerRecipeLogic.java +++ b/src/main/java/gregtech/api/capability/impl/BoilerRecipeLogic.java @@ -49,8 +49,6 @@ public class BoilerRecipeLogic extends AbstractRecipeLogic implements ICategoryO public BoilerRecipeLogic(MetaTileEntityLargeBoiler tileEntity) { super(tileEntity, null); - this.fluidOutputs = Collections.emptyList(); - this.itemOutputs = Collections.emptyList(); } @Override diff --git a/src/main/java/gregtech/api/capability/impl/MultiblockFuelRecipeLogic.java b/src/main/java/gregtech/api/capability/impl/MultiblockFuelRecipeLogic.java index 4c374985014..324934fb394 100644 --- a/src/main/java/gregtech/api/capability/impl/MultiblockFuelRecipeLogic.java +++ b/src/main/java/gregtech/api/capability/impl/MultiblockFuelRecipeLogic.java @@ -23,6 +23,7 @@ public class MultiblockFuelRecipeLogic extends MultiblockRecipeLogic { protected long totalContinuousRunningTime; + private int previousDuration = 0; public MultiblockFuelRecipeLogic(RecipeMapMultiblockController tileEntity) { super(tileEntity); @@ -127,18 +128,24 @@ public String getRecipeFluidInputInfo() { } else { recipe = previousRecipe; } + previousDuration = recipe.getDuration(); FluidStack requiredFluidInput = recipe.getFluidInputs().get(0).getInputFluidStack(); int ocAmount = GTUtility.safeCastLongToInt(getMaxVoltage() / recipe.getEUt()); int neededAmount = ocAmount * requiredFluidInput.amount; if (rotorHolder != null && rotorHolder.hasRotor()) { - neededAmount /= (rotorHolder.getTotalEfficiency() / 100.0); + neededAmount /= (int) (rotorHolder.getTotalEfficiency() / 100.0); } else if (rotorHolder != null && !rotorHolder.hasRotor()) { return null; } return TextFormatting.RED + TextFormattingUtil.formatNumbers(neededAmount) + "L"; } + @Override + public int getPreviousRecipeDuration() { + return previousDuration; + } + public FluidStack getInputFluidStack() { // Previous Recipe is always null on first world load, so try to acquire a new recipe if (previousRecipe == null) { diff --git a/src/main/java/gregtech/api/fluids/GTFluid.java b/src/main/java/gregtech/api/fluids/GTFluid.java index 5b808f034f8..f06fafaf21d 100644 --- a/src/main/java/gregtech/api/fluids/GTFluid.java +++ b/src/main/java/gregtech/api/fluids/GTFluid.java @@ -4,7 +4,6 @@ import gregtech.api.fluids.attribute.FluidAttribute; import gregtech.api.unification.material.Material; -import net.minecraft.client.resources.I18n; import net.minecraft.util.ResourceLocation; import net.minecraft.util.text.TextComponentTranslation; import net.minecraftforge.fluids.Fluid; @@ -12,7 +11,9 @@ import net.minecraftforge.fml.relauncher.Side; import net.minecraftforge.fml.relauncher.SideOnly; +import com.cleanroommc.modularui.api.drawable.IKey; import it.unimi.dsi.fastutil.objects.ObjectLinkedOpenHashSet; +import org.jetbrains.annotations.ApiStatus; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import org.jetbrains.annotations.Unmodifiable; @@ -62,6 +63,8 @@ public GTMaterialFluid(@NotNull String fluidName, ResourceLocation still, Resour return this.material; } + @Deprecated + @ApiStatus.ScheduledForRemoval(inVersion = "2.10") public @NotNull TextComponentTranslation toTextComponentTranslation() { TextComponentTranslation localizedName; String customMaterialTranslation = "fluid." + material.getUnlocalizedName(); @@ -78,22 +81,27 @@ public GTMaterialFluid(@NotNull String fluidName, ResourceLocation still, Resour return localizedName; } - @Override - @SideOnly(Side.CLIENT) - public String getLocalizedName(FluidStack stack) { - String localizedName; + public @NotNull IKey getLocalizedKey() { + IKey localizedName; String customMaterialTranslation = "fluid." + material.getUnlocalizedName(); - if (I18n.hasKey(customMaterialTranslation)) { - localizedName = I18n.format(customMaterialTranslation); + if (net.minecraft.util.text.translation.I18n.canTranslate(customMaterialTranslation)) { + localizedName = IKey.lang(customMaterialTranslation); } else { - localizedName = I18n.format(material.getUnlocalizedName()); + localizedName = IKey.lang(material.getUnlocalizedName()); } if (translationKey != null) { - return I18n.format(translationKey, localizedName); + return IKey.lang(translationKey, localizedName); } + return localizedName; } + + @Override + @SideOnly(Side.CLIENT) + public String getLocalizedName(FluidStack stack) { + return getLocalizedKey().get(); + } } } diff --git a/src/main/java/gregtech/api/gui/widgets/ProgressWidget.java b/src/main/java/gregtech/api/gui/widgets/ProgressWidget.java index b4699c241fd..a68f1bfcb2d 100644 --- a/src/main/java/gregtech/api/gui/widgets/ProgressWidget.java +++ b/src/main/java/gregtech/api/gui/widgets/ProgressWidget.java @@ -5,6 +5,7 @@ import gregtech.api.gui.resources.TextureArea; import gregtech.api.util.Position; import gregtech.api.util.Size; +import gregtech.api.util.function.impl.TimedProgressSupplier; import gregtech.common.ConfigHolder; import net.minecraft.client.renderer.GlStateManager; @@ -295,38 +296,4 @@ public void drawInForeground(int mouseX, int mouseY) { } } } - - public static class TimedProgressSupplier implements DoubleSupplier { - - private final int msPerCycle; - private final int maxValue; - private final boolean countDown; - private long startTime; - - public TimedProgressSupplier(int ticksPerCycle, int maxValue, boolean countDown) { - this.msPerCycle = ticksPerCycle * 50; - this.maxValue = maxValue; - this.countDown = countDown; - this.startTime = System.currentTimeMillis(); - } - - public void resetCountdown() { - startTime = System.currentTimeMillis(); - } - - @Override - public double getAsDouble() { - return calculateTime(); - } - - private double calculateTime() { - long currentTime = System.currentTimeMillis(); - long msPassed = (currentTime - startTime) % msPerCycle; - double currentValue = 1.0 * msPassed * maxValue / msPerCycle; - if (countDown) { - return (maxValue - currentValue) / maxValue; - } - return currentValue / maxValue; - } - } } diff --git a/src/main/java/gregtech/api/metatileentity/MetaTileEntity.java b/src/main/java/gregtech/api/metatileentity/MetaTileEntity.java index 308bf0c8aaf..c39b082e68a 100644 --- a/src/main/java/gregtech/api/metatileentity/MetaTileEntity.java +++ b/src/main/java/gregtech/api/metatileentity/MetaTileEntity.java @@ -492,7 +492,7 @@ public GTGuiTheme getUITheme() { } @Override - public ModularPanel buildUI(PosGuiData guiData, PanelSyncManager guiSyncManager) { + public ModularPanel buildUI(PosGuiData guiData, PanelSyncManager panelSyncManager) { return null; } @@ -892,7 +892,11 @@ private void updateSound() { if (sound == null) { return; } - if (isValid() && isActive()) { + boolean canPlay = isValid() && isActive(); + if (this instanceof IControllable controllable) { + canPlay &= controllable.isWorkingEnabled(); + } + if (canPlay) { if (--playSoundCooldown > 0) { return; } diff --git a/src/main/java/gregtech/api/metatileentity/multiblock/FuelMultiblockController.java b/src/main/java/gregtech/api/metatileentity/multiblock/FuelMultiblockController.java index fb3a1993fc2..2a8a632b5c5 100644 --- a/src/main/java/gregtech/api/metatileentity/multiblock/FuelMultiblockController.java +++ b/src/main/java/gregtech/api/metatileentity/multiblock/FuelMultiblockController.java @@ -5,6 +5,8 @@ import gregtech.api.capability.IMultipleTankHandler; import gregtech.api.capability.impl.EnergyContainerList; import gregtech.api.capability.impl.MultiblockFuelRecipeLogic; +import gregtech.api.metatileentity.multiblock.ui.MultiblockUIBuilder; +import gregtech.api.mui.sync.FixedIntArraySyncValue; import gregtech.api.recipes.RecipeMap; import gregtech.api.util.GTUtility; import gregtech.api.util.TextComponentUtil; @@ -16,8 +18,13 @@ import net.minecraft.util.text.Style; import net.minecraft.util.text.TextComponentTranslation; import net.minecraft.util.text.TextFormatting; +import net.minecraftforge.fluids.Fluid; +import net.minecraftforge.fluids.FluidRegistry; import net.minecraftforge.fluids.FluidStack; +import com.cleanroommc.modularui.api.drawable.IKey; +import com.cleanroommc.modularui.screen.RichTooltip; +import com.cleanroommc.modularui.value.sync.StringSyncValue; import org.jetbrains.annotations.NotNull; import java.util.ArrayList; @@ -41,16 +48,22 @@ protected void initializeAbilities() { } @Override - protected void addDisplayText(List textList) { + protected void configureDisplayText(MultiblockUIBuilder builder) { MultiblockFuelRecipeLogic recipeLogic = (MultiblockFuelRecipeLogic) recipeMapWorkable; - MultiblockDisplayText.builder(textList, isStructureFormed()) - .setWorkingStatus(recipeLogic.isWorkingEnabled(), recipeLogic.isActive()) + builder.setWorkingStatus(recipeLogic.isWorkingEnabled(), recipeLogic.isActive()) .addEnergyProductionLine(getMaxVoltage(), recipeLogic.getRecipeEUt()) .addFuelNeededLine(recipeLogic.getRecipeFluidInputInfo(), recipeLogic.getPreviousRecipeDuration()) .addWorkingStatusLine(); } + @Override + protected void configureWarningText(MultiblockUIBuilder builder) { + builder.addLowDynamoTierLine(isDynamoTierTooLow()); + if (hasMaintenanceMechanics()) + builder.addMaintenanceProblemLines(getMaintenanceProblems(), true); + } + protected long getMaxVoltage() { IEnergyContainer energyContainer = recipeMapWorkable.getEnergyContainer(); if (energyContainer != null && energyContainer.getEnergyCapacity() > 0) { @@ -60,13 +73,6 @@ protected long getMaxVoltage() { } } - @Override - protected void addWarningText(List textList) { - MultiblockDisplayText.builder(textList, isStructureFormed(), false) - .addLowDynamoTierLine(isDynamoTierTooLow()) - .addMaintenanceProblemLines(getMaintenanceProblems()); - } - protected boolean isDynamoTierTooLow() { if (isStructureFormed()) { IEnergyContainer energyContainer = recipeMapWorkable.getEnergyContainer(); @@ -138,6 +144,7 @@ protected int[] getTotalFluidAmount(FluidStack testStack, IMultipleTankHandler m return new int[] { fluidAmount, fluidCapacity }; } + @Deprecated protected void addFuelText(List textList) { // Fuel int fuelStored = 0; @@ -170,4 +177,26 @@ protected void addFuelText(List textList) { "0 / 0 L")); } } + + /** + * @param tooltip the tooltip to populate + * @param amounts the sync value containing an array of [fuel stored, fuel capacity] + * @param fuelNameValue the name of the fuel + */ + protected void createFuelTooltip(@NotNull RichTooltip tooltip, @NotNull FixedIntArraySyncValue amounts, + @NotNull StringSyncValue fuelNameValue) { + if (isStructureFormed()) { + Fluid fluid = fuelNameValue.getStringValue() == null ? null : + FluidRegistry.getFluid(fuelNameValue.getStringValue()); + if (fluid == null) { + tooltip.addLine(IKey.lang("gregtech.multiblock.large_combustion_engine.fuel_none")); + } else { + tooltip.addLine( + IKey.lang("gregtech.multiblock.large_combustion_engine.fuel_amount", amounts.getValue(0), + amounts.getValue(1), fluid.getLocalizedName(new FluidStack(fluid, 1)))); + } + } else { + tooltip.addLine(IKey.lang("gregtech.multiblock.invalid_structure")); + } + } } diff --git a/src/main/java/gregtech/api/metatileentity/multiblock/IProgressBarMultiblock.java b/src/main/java/gregtech/api/metatileentity/multiblock/IProgressBarMultiblock.java index 77567e09af2..d16cc5d7c3c 100644 --- a/src/main/java/gregtech/api/metatileentity/multiblock/IProgressBarMultiblock.java +++ b/src/main/java/gregtech/api/metatileentity/multiblock/IProgressBarMultiblock.java @@ -5,8 +5,12 @@ import net.minecraft.util.text.ITextComponent; +import org.jetbrains.annotations.ApiStatus; + import java.util.List; +@ApiStatus.ScheduledForRemoval(inVersion = "2.10") +@Deprecated public interface IProgressBarMultiblock { default boolean showProgressBar() { @@ -24,6 +28,7 @@ default int getNumProgressBars() { double getFillPercentage(int index); /** Textures for the progress bar(s). */ + @Deprecated default TextureArea getProgressBarTexture(int index) { return GuiTextures.PROGRESS_BAR_MULTI_ENERGY_YELLOW; } @@ -33,5 +38,6 @@ default TextureArea getProgressBarTexture(int index) { * * @param index The index, 0, 1, or 2, of your progress bar. Only relevant if you have multiple bars. */ + @Deprecated default void addBarHoverText(List hoverList, int index) {} } diff --git a/src/main/java/gregtech/api/metatileentity/multiblock/MultiMapMultiblockController.java b/src/main/java/gregtech/api/metatileentity/multiblock/MultiMapMultiblockController.java index f80337e10c8..ded9c69269c 100644 --- a/src/main/java/gregtech/api/metatileentity/multiblock/MultiMapMultiblockController.java +++ b/src/main/java/gregtech/api/metatileentity/multiblock/MultiMapMultiblockController.java @@ -3,12 +3,12 @@ import gregtech.api.capability.GregtechDataCodes; import gregtech.api.capability.GregtechTileCapabilities; import gregtech.api.capability.IMultipleRecipeMaps; -import gregtech.api.gui.GuiTextures; -import gregtech.api.gui.Widget; -import gregtech.api.gui.widgets.ImageCycleButtonWidget; +import gregtech.api.metatileentity.multiblock.ui.MultiblockUIBuilder; +import gregtech.api.metatileentity.multiblock.ui.MultiblockUIFactory; +import gregtech.api.mui.GTGuiTextures; import gregtech.api.pattern.TraceabilityPredicate; import gregtech.api.recipes.RecipeMap; -import gregtech.api.util.LocalizationUtils; +import gregtech.api.util.GTUtility; import net.minecraft.client.resources.I18n; import net.minecraft.entity.player.EntityPlayer; @@ -25,7 +25,10 @@ import net.minecraftforge.fml.relauncher.SideOnly; import codechicken.lib.raytracer.CuboidRayTraceResult; -import org.jetbrains.annotations.NotNull; +import com.cleanroommc.modularui.api.drawable.IKey; +import com.cleanroommc.modularui.value.sync.IntSyncValue; +import com.cleanroommc.modularui.widgets.CycleButtonWidget; +import org.apache.commons.lang3.ArrayUtils; import org.jetbrains.annotations.Nullable; import java.util.List; @@ -133,17 +136,39 @@ public TraceabilityPredicate autoAbilities(boolean checkEnergyIn, boolean checkM } @Override - protected @NotNull Widget getFlexButton(int x, int y, int width, int height) { - if (getAvailableRecipeMaps() != null && getAvailableRecipeMaps().length > 1) { - return new ImageCycleButtonWidget(x, y, width, height, GuiTextures.BUTTON_MULTI_MAP, - getAvailableRecipeMaps().length, this::getRecipeMapIndex, this::setRecipeMapIndex) - .shouldUseBaseBackground().singleTexture() - .setTooltipHoverString(i -> LocalizationUtils - .format("gregtech.multiblock.multiple_recipemaps.header") + " " + - LocalizationUtils.format( - "recipemap." + getAvailableRecipeMaps()[i].getUnlocalizedName() + ".name")); - } - return super.getFlexButton(x, y, width, height); + protected MultiblockUIFactory createUIFactory() { + return super.createUIFactory() + .createFlexButton((guiData, syncManager) -> { + RecipeMap[] recipeMaps = getAvailableRecipeMaps(); + if (ArrayUtils.getLength(recipeMaps) <= 1) return null; + + IntSyncValue activeMapIndex = new IntSyncValue(this::getRecipeMapIndex, this::setRecipeMapIndex); + + return new CycleButtonWidget() + .overlay(GTGuiTextures.BUTTON_MULTI_MAP) + .background(GTGuiTextures.BUTTON) + // TODO find out why this needs to be called + .disableHoverBackground() + .value(activeMapIndex) + .length(recipeMaps.length) + .tooltipBuilder(t -> { + RecipeMap map = recipeMaps[activeMapIndex.getIntValue()]; + t.addLine(IKey.lang("gregtech.multiblock.multiple_recipemaps.value", + map.getTranslationKey())); + }); + }); + } + + @Override + protected void configureDisplayText(MultiblockUIBuilder builder) { + builder.setWorkingStatus(recipeMapWorkable.isWorkingEnabled(), recipeMapWorkable.isActive()) + .addRecipeMapLine(getCurrentRecipeMap()) + .addEnergyUsageLine(this.getEnergyContainer()) + .addEnergyTierLine(GTUtility.getTierByVoltage(recipeMapWorkable.getMaxVoltage())) + .addParallelsLine(recipeMapWorkable.getParallelLimit()) + .addWorkingStatusLine() + .addProgressLine(recipeMapWorkable.getProgress(), recipeMapWorkable.getMaxProgress()) + .addRecipeOutputLine(recipeMapWorkable); } @Override diff --git a/src/main/java/gregtech/api/metatileentity/multiblock/MultiblockDisplayText.java b/src/main/java/gregtech/api/metatileentity/multiblock/MultiblockDisplayText.java deleted file mode 100644 index e7b5d68e6d2..00000000000 --- a/src/main/java/gregtech/api/metatileentity/multiblock/MultiblockDisplayText.java +++ /dev/null @@ -1,482 +0,0 @@ -package gregtech.api.metatileentity.multiblock; - -import gregtech.api.GTValues; -import gregtech.api.capability.IEnergyContainer; -import gregtech.api.util.GTUtility; -import gregtech.api.util.TextComponentUtil; -import gregtech.api.util.TextFormattingUtil; -import gregtech.common.ConfigHolder; - -import net.minecraft.util.text.*; - -import java.util.List; -import java.util.function.Consumer; - -public class MultiblockDisplayText { - - private static final ITextComponent EMPTY_COMPONENT = new TextComponentString(""); - - /** - * Construct a new Multiblock Display Text builder. - *
- * Automatically adds the "Invalid Structure" line if the structure is not formed. - */ - public static Builder builder(List textList, boolean isStructureFormed) { - return builder(textList, isStructureFormed, true); - } - - public static Builder builder(List textList, boolean isStructureFormed, - boolean showIncompleteStructureWarning) { - return new Builder(textList, isStructureFormed, showIncompleteStructureWarning); - } - - public static class Builder { - - private final List textList; - private final boolean isStructureFormed; - - private boolean isWorkingEnabled, isActive; - - // Keys for the three-state working system, can be set custom by multiblocks. - private String idlingKey = "gregtech.multiblock.idling"; - private String pausedKey = "gregtech.multiblock.work_paused"; - private String runningKey = "gregtech.multiblock.running"; - - private Builder(List textList, boolean isStructureFormed, - boolean showIncompleteStructureWarning) { - this.textList = textList; - this.isStructureFormed = isStructureFormed; - - if (!isStructureFormed && showIncompleteStructureWarning) { - ITextComponent base = TextComponentUtil.translationWithColor(TextFormatting.RED, - "gregtech.multiblock.invalid_structure"); - ITextComponent hover = TextComponentUtil.translationWithColor(TextFormatting.GRAY, - "gregtech.multiblock.invalid_structure.tooltip"); - textList.add(TextComponentUtil.setHover(base, hover)); - } - } - - /** Set the current working enabled and active status of this multiblock, used by many line addition calls. */ - public Builder setWorkingStatus(boolean isWorkingEnabled, boolean isActive) { - this.isWorkingEnabled = isWorkingEnabled; - this.isActive = isActive; - return this; - } - - /** - * Set custom translation keys for the three-state "Idling", "Paused", "Running" display text. - * You still must call {@link Builder#addWorkingStatusLine()} for these to appear! - *
- * Pass any key as null for it to continue to use the default key. - * - * @param idlingKey The translation key for the Idle state, or "!isActive && isWorkingEnabled". - * @param pausedKey The translation key for the Paused state, or "!isWorkingEnabled". - * @param runningKey The translation key for the Running state, or "isActive". - */ - public Builder setWorkingStatusKeys(String idlingKey, String pausedKey, String runningKey) { - if (idlingKey != null) this.idlingKey = idlingKey; - if (pausedKey != null) this.pausedKey = pausedKey; - if (runningKey != null) this.runningKey = runningKey; - return this; - } - - /** - * Adds the max EU/t that this multiblock can use. - *
- * Added if the structure is formed and if the passed energy container has greater than zero capacity. - */ - public Builder addEnergyUsageLine(IEnergyContainer energyContainer) { - if (!isStructureFormed) return this; - if (energyContainer != null && energyContainer.getEnergyCapacity() > 0) { - long maxVoltage = Math.max(energyContainer.getInputVoltage(), energyContainer.getOutputVoltage()); - - String energyFormatted = TextFormattingUtil.formatNumbers(maxVoltage); - // wrap in text component to keep it from being formatted - ITextComponent voltageName = new TextComponentString( - GTValues.VOCNF[GTUtility.getFloorTierByVoltage(maxVoltage)]); - - ITextComponent bodyText = TextComponentUtil.translationWithColor( - TextFormatting.GRAY, - "gregtech.multiblock.max_energy_per_tick", - energyFormatted, voltageName); - ITextComponent hoverText = TextComponentUtil.translationWithColor(TextFormatting.GRAY, - "gregtech.multiblock.max_energy_per_tick_hover"); - textList.add(TextComponentUtil.setHover(bodyText, hoverText)); - } - return this; - } - - /** - * Adds the max Recipe Tier that this multiblock can use for recipe lookup. - *
- * Added if the structure is formed and if the passed tier is a valid energy tier index for - * {@link GTValues#VNF}. - */ - public Builder addEnergyTierLine(int tier) { - if (!isStructureFormed) return this; - if (tier < GTValues.ULV || tier > GTValues.MAX) return this; - - ITextComponent voltageName = new TextComponentString(GTValues.VNF[tier]); - ITextComponent bodyText = TextComponentUtil.translationWithColor( - TextFormatting.GRAY, - "gregtech.multiblock.max_recipe_tier", - voltageName); - ITextComponent hoverText = TextComponentUtil.translationWithColor(TextFormatting.GRAY, - "gregtech.multiblock.max_recipe_tier_hover"); - textList.add(TextComponentUtil.setHover(bodyText, hoverText)); - return this; - } - - /** - * Adds the exact EU/t that this multiblock needs to run. - *
- * Added if the structure is formed and if the passed value is greater than zero. - */ - public Builder addEnergyUsageExactLine(long energyUsage) { - if (!isStructureFormed) return this; - if (energyUsage > 0) { - String energyFormatted = TextFormattingUtil.formatNumbers(energyUsage); - // wrap in text component to keep it from being formatted - ITextComponent voltageName = new TextComponentString( - GTValues.VOCNF[GTUtility.getOCTierByVoltage(energyUsage)]); - - textList.add(TextComponentUtil.translationWithColor( - TextFormatting.GRAY, - "gregtech.multiblock.energy_consumption", - energyFormatted, voltageName)); - } - return this; - } - - /** - * Adds the max EU/t that this multiblock can produce. - *
- * Added if the structure is formed and if the max voltage is greater than zero and the recipe EU/t. - */ - public Builder addEnergyProductionLine(long maxVoltage, long recipeEUt) { - if (!isStructureFormed) return this; - if (maxVoltage != 0 && maxVoltage >= recipeEUt) { - String energyFormatted = TextFormattingUtil.formatNumbers(maxVoltage); - // wrap in text component to keep it from being formatted - ITextComponent voltageName = new TextComponentString( - GTValues.VOCNF[GTUtility.getFloorTierByVoltage(maxVoltage)]); - - textList.add(TextComponentUtil.translationWithColor( - TextFormatting.GRAY, - "gregtech.multiblock.max_energy_per_tick", - energyFormatted, voltageName)); - } - return this; - } - - /** - * Adds the max EU/t that this multiblock can produce, including how many amps. Recommended for multi-amp - * outputting multis. - *
- * Added if the structure is formed, if the amperage is greater than zero and if the max voltage is greater than - * zero. - */ - public Builder addEnergyProductionAmpsLine(long maxVoltage, int amperage) { - if (!isStructureFormed) return this; - if (maxVoltage != 0 && amperage != 0) { - String energyFormatted = TextFormattingUtil.formatNumbers(maxVoltage); - // wrap in text component to keep it from being formatted - ITextComponent voltageName = new TextComponentString( - GTValues.VOCNF[GTUtility.getFloorTierByVoltage(maxVoltage)]); - - textList.add(TextComponentUtil.translationWithColor( - TextFormatting.GRAY, - "gregtech.multiblock.max_energy_per_tick_amps", - energyFormatted, amperage, voltageName)); - } - return this; - } - - /** - * Adds the max CWU/t that this multiblock can use. - *
- * Added if the structure is formed and if the max CWU/t is greater than zero. - */ - public Builder addComputationUsageLine(int maxCWUt) { - if (!isStructureFormed) return this; - if (maxCWUt > 0) { - ITextComponent computation = TextComponentUtil.stringWithColor(TextFormatting.AQUA, - TextFormattingUtil.formatNumbers(maxCWUt)); - textList.add(TextComponentUtil.translationWithColor( - TextFormatting.GRAY, - "gregtech.multiblock.computation.max", - computation)); - } - return this; - } - - /** - * Adds a currently used CWU/t line. - *
- * Added if the structure is formed, the machine is active, and the current CWU/t is greater than zero. - */ - public Builder addComputationUsageExactLine(int currentCWUt) { - if (!isStructureFormed) return this; - if (isActive && currentCWUt > 0) { - ITextComponent computation = TextComponentUtil.stringWithColor(TextFormatting.AQUA, - TextFormattingUtil.formatNumbers(currentCWUt) + " CWU/t"); - textList.add(TextComponentUtil.translationWithColor( - TextFormatting.GRAY, - "gregtech.multiblock.computation.usage", - computation)); - } - return this; - } - - /** - * Adds a three-state indicator line, showing if the machine is running, paused, or idling. - *
- * Added if the structure is formed. - */ - public Builder addWorkingStatusLine() { - if (!isStructureFormed) return this; - - if (!isWorkingEnabled) { - return addWorkPausedLine(false); - } else if (isActive) { - return addRunningPerfectlyLine(false); - } else { - return addIdlingLine(false); - } - } - - /** - * Adds the "Work Paused." line. - *
- * Added if working is not enabled, or if the checkState passed parameter is false. - * Also added only if formed. - */ - public Builder addWorkPausedLine(boolean checkState) { - if (!isStructureFormed) return this; - if (!checkState || !isWorkingEnabled) { - textList.add(TextComponentUtil.translationWithColor(TextFormatting.GOLD, pausedKey)); - } - return this; - } - - /** - * Adds the "Running Perfectly." line. - *
- * Added if machine is active, or if the checkState passed parameter is false. - * Also added only if formed. - */ - public Builder addRunningPerfectlyLine(boolean checkState) { - if (!isStructureFormed) return this; - if (!checkState || isActive) { - textList.add(TextComponentUtil.translationWithColor(TextFormatting.GREEN, runningKey)); - } - return this; - } - - /** - * Adds the "Idling." line. - *
- * Added if the machine is not active and working is enabled, or if the checkState passed parameter is false. - * Also added only if formed. - */ - public Builder addIdlingLine(boolean checkState) { - if (!isStructureFormed) return this; - if (!checkState || (isWorkingEnabled && !isActive)) { - textList.add(TextComponentUtil.translationWithColor(TextFormatting.GRAY, idlingKey)); - } - return this; - } - - /** - * Adds a simple progress line that displays progress as a 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. - */ - public Builder addProgressLine(double progressPercent) { // todo - if (!isStructureFormed || !isActive) return this; - int currentProgress = (int) (progressPercent * 100); - textList.add(new TextComponentTranslation("gregtech.multiblock.progress", currentProgress)); - 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. - */ - public Builder addParallelsLine(int numParallels) { - if (!isStructureFormed) return this; - if (numParallels > 1) { - ITextComponent parallels = TextComponentUtil.stringWithColor( - TextFormatting.DARK_PURPLE, - TextFormattingUtil.formatNumbers(numParallels)); - - textList.add(TextComponentUtil.translationWithColor( - TextFormatting.GRAY, - "gregtech.multiblock.parallel", - parallels)); - } - return this; - } - - /** - * Adds a warning line when the machine is low on power. - *
- * Added if the structure is formed and if the passed parameter is true. - */ - public Builder addLowPowerLine(boolean isLowPower) { - if (!isStructureFormed) return this; - if (isLowPower) { - textList.add(TextComponentUtil.translationWithColor(TextFormatting.YELLOW, - "gregtech.multiblock.not_enough_energy")); - } - return this; - } - - /** - * Adds a warning line when the machine is low on computation. - *
- * Added if the structure is formed and if the passed parameter is true. - */ - public Builder addLowComputationLine(boolean isLowComputation) { - if (!isStructureFormed) return this; - if (isLowComputation) { - textList.add(TextComponentUtil.translationWithColor(TextFormatting.YELLOW, - "gregtech.multiblock.computation.not_enough_computation")); - } - return this; - } - - /** - * Adds a warning line when the machine's dynamo tier is too low for current conditions. - *
- * Added if the structure is formed and if the passed parameter is true. - */ - public Builder addLowDynamoTierLine(boolean isTooLow) { - if (!isStructureFormed) return this; - if (isTooLow) { - textList.add(TextComponentUtil.translationWithColor(TextFormatting.YELLOW, - "gregtech.multiblock.not_enough_energy_output")); - } - return this; - } - - /** - * Adds warning line(s) when the machine has maintenance problems. - *
- * Added if there are any maintenance problems, one line per problem as well as a header.
- * Will check the config setting for if maintenance is enabled automatically. - */ - public Builder addMaintenanceProblemLines(byte maintenanceProblems) { - if (!isStructureFormed || !ConfigHolder.machines.enableMaintenance) return this; - if (maintenanceProblems < 63) { - boolean hasAddedHeader = false; - - // Wrench - if ((maintenanceProblems & 1) == 0) { - hasAddedHeader = addMaintenanceProblemHeader(hasAddedHeader); - textList.add(TextComponentUtil.translationWithColor( - TextFormatting.GRAY, - "gregtech.multiblock.universal.problem.wrench")); - } - - // Screwdriver - if (((maintenanceProblems >> 1) & 1) == 0) { - hasAddedHeader = addMaintenanceProblemHeader(hasAddedHeader); - textList.add(TextComponentUtil.translationWithColor( - TextFormatting.GRAY, - "gregtech.multiblock.universal.problem.screwdriver")); - } - - // Soft Mallet - if (((maintenanceProblems >> 2) & 1) == 0) { - hasAddedHeader = addMaintenanceProblemHeader(hasAddedHeader); - textList.add(TextComponentUtil.translationWithColor( - TextFormatting.GRAY, - "gregtech.multiblock.universal.problem.soft_mallet")); - } - - // Hammer - if (((maintenanceProblems >> 3) & 1) == 0) { - hasAddedHeader = addMaintenanceProblemHeader(hasAddedHeader); - textList.add(TextComponentUtil.translationWithColor( - TextFormatting.GRAY, - "gregtech.multiblock.universal.problem.hard_hammer")); - } - - // Wire Cutters - if (((maintenanceProblems >> 4) & 1) == 0) { - hasAddedHeader = addMaintenanceProblemHeader(hasAddedHeader); - textList.add(TextComponentUtil.translationWithColor( - TextFormatting.GRAY, - "gregtech.multiblock.universal.problem.wire_cutter")); - } - - // Crowbar - if (((maintenanceProblems >> 5) & 1) == 0) { - addMaintenanceProblemHeader(hasAddedHeader); - textList.add(TextComponentUtil.translationWithColor( - TextFormatting.GRAY, - "gregtech.multiblock.universal.problem.crowbar")); - } - } - return this; - } - - private boolean addMaintenanceProblemHeader(boolean hasAddedHeader) { - if (!hasAddedHeader) { - textList.add(TextComponentUtil.translationWithColor( - TextFormatting.YELLOW, - "gregtech.multiblock.universal.has_problems")); - } - return true; - } - - /** - * Adds two error lines when the machine's muffler hatch is obstructed. - *
- * Added if the structure is formed and if the passed parameter is true. - */ - public Builder addMufflerObstructedLine(boolean isObstructed) { - if (!isStructureFormed) return this; - if (isObstructed) { - textList.add(TextComponentUtil.translationWithColor(TextFormatting.RED, - "gregtech.multiblock.universal.muffler_obstructed")); - textList.add(TextComponentUtil.translationWithColor(TextFormatting.GRAY, - "gregtech.multiblock.universal.muffler_obstructed_desc")); - } - return this; - } - - /** - * Adds a fuel consumption line showing the fuel name and the number of ticks per recipe run. - *
- * Added if structure is formed, the machine is active, and the passed fuelName parameter is not null. - */ - public Builder addFuelNeededLine(String fuelName, int previousRecipeDuration) { - if (!isStructureFormed || !isActive || fuelName == null) return this; - ITextComponent fuelNeeded = TextComponentUtil.stringWithColor(TextFormatting.RED, fuelName); - ITextComponent numTicks = TextComponentUtil.stringWithColor(TextFormatting.AQUA, - TextFormattingUtil.formatNumbers(previousRecipeDuration)); - textList.add(TextComponentUtil.translationWithColor( - TextFormatting.GRAY, - "gregtech.multiblock.turbine.fuel_needed", - fuelNeeded, numTicks)); - return this; - } - - /** Insert an empty line into the text list. */ - public Builder addEmptyLine() { - textList.add(EMPTY_COMPONENT); - return this; - } - - /** Add custom text dynamically, allowing for custom application logic. */ - public Builder addCustom(Consumer> customConsumer) { - customConsumer.accept(textList); - return this; - } - } -} diff --git a/src/main/java/gregtech/api/metatileentity/multiblock/MultiblockWithDisplayBase.java b/src/main/java/gregtech/api/metatileentity/multiblock/MultiblockWithDisplayBase.java index 46b3c774658..1f31dce42a0 100644 --- a/src/main/java/gregtech/api/metatileentity/multiblock/MultiblockWithDisplayBase.java +++ b/src/main/java/gregtech/api/metatileentity/multiblock/MultiblockWithDisplayBase.java @@ -3,16 +3,8 @@ import gregtech.api.GTValues; import gregtech.api.block.VariantActiveBlock; import gregtech.api.capability.*; -import gregtech.api.gui.GuiTextures; -import gregtech.api.gui.ModularUI; -import gregtech.api.gui.Widget; -import gregtech.api.gui.Widget.ClickData; -import gregtech.api.gui.resources.TextureArea; -import gregtech.api.gui.widgets.AdvancedTextWidget; -import gregtech.api.gui.widgets.ImageCycleButtonWidget; -import gregtech.api.gui.widgets.ImageWidget; -import gregtech.api.gui.widgets.IndicatorImageWidget; -import gregtech.api.gui.widgets.ProgressWidget; +import gregtech.api.metatileentity.multiblock.ui.MultiblockUIBuilder; +import gregtech.api.metatileentity.multiblock.ui.MultiblockUIFactory; import gregtech.api.pattern.PatternMatchContext; import gregtech.api.pattern.TraceabilityPredicate; import gregtech.api.unification.OreDictUnifier; @@ -20,19 +12,20 @@ import gregtech.api.unification.ore.OrePrefix; import gregtech.common.ConfigHolder; -import net.minecraft.entity.player.EntityPlayer; import net.minecraft.item.ItemStack; import net.minecraft.nbt.NBTTagCompound; import net.minecraft.network.PacketBuffer; import net.minecraft.util.*; import net.minecraft.util.math.BlockPos; -import net.minecraft.util.text.ITextComponent; import net.minecraftforge.common.capabilities.Capability; import net.minecraftforge.fluids.IFluidTank; import net.minecraftforge.fluids.capability.IFluidHandler; import net.minecraftforge.fml.relauncher.Side; import net.minecraftforge.fml.relauncher.SideOnly; +import com.cleanroommc.modularui.factory.PosGuiData; +import com.cleanroommc.modularui.screen.ModularPanel; +import com.cleanroommc.modularui.value.sync.PanelSyncManager; import org.jetbrains.annotations.NotNull; import java.util.*; @@ -45,6 +38,7 @@ public abstract class MultiblockWithDisplayBase extends MultiblockControllerBase private static final String NBT_VOIDING_MODE = "VoidingMode"; private static final String NBT_VOIDING_ITEMS = "VoidingItems"; private static final String NBT_VOIDING_FLUIDS = "VoidingFluids"; + private MultiblockUIFactory uiFactory; private boolean voidingItems = false; private boolean voidingFluids = false; @@ -342,180 +336,15 @@ protected TraceabilityPredicate maintenancePredicate() { return new TraceabilityPredicate(); } - /** - * Called serverside to obtain text displayed in GUI - * each element of list is displayed on new line - * to use translation, use TextComponentTranslation - */ - protected void addDisplayText(List textList) { - MultiblockDisplayText.builder(textList, isStructureFormed()); - } - - /** - * Called on serverside when client is clicked on the specific text component - * with special click event handler - * Data is the data specified in the component - */ - protected void handleDisplayClick(String componentData, ClickData clickData) {} - - protected ModularUI.Builder createUITemplate(EntityPlayer entityPlayer) { - ModularUI.Builder builder = ModularUI.builder(GuiTextures.BACKGROUND, 198, 208); - - // Display - if (this instanceof IProgressBarMultiblock progressMulti && progressMulti.showProgressBar()) { - builder.image(4, 4, 190, 109, GuiTextures.DISPLAY); - - if (progressMulti.getNumProgressBars() == 3) { - // triple bar - ProgressWidget progressBar = new ProgressWidget( - () -> progressMulti.getFillPercentage(0), - 4, 115, 62, 7, - progressMulti.getProgressBarTexture(0), ProgressWidget.MoveType.HORIZONTAL) - .setHoverTextConsumer(list -> progressMulti.addBarHoverText(list, 0)); - builder.widget(progressBar); - - progressBar = new ProgressWidget( - () -> progressMulti.getFillPercentage(1), - 68, 115, 62, 7, - progressMulti.getProgressBarTexture(1), ProgressWidget.MoveType.HORIZONTAL) - .setHoverTextConsumer(list -> progressMulti.addBarHoverText(list, 1)); - builder.widget(progressBar); - - progressBar = new ProgressWidget( - () -> progressMulti.getFillPercentage(2), - 132, 115, 62, 7, - progressMulti.getProgressBarTexture(2), ProgressWidget.MoveType.HORIZONTAL) - .setHoverTextConsumer(list -> progressMulti.addBarHoverText(list, 2)); - builder.widget(progressBar); - } else if (progressMulti.getNumProgressBars() == 2) { - // double bar - ProgressWidget progressBar = new ProgressWidget( - () -> progressMulti.getFillPercentage(0), - 4, 115, 94, 7, - progressMulti.getProgressBarTexture(0), ProgressWidget.MoveType.HORIZONTAL) - .setHoverTextConsumer(list -> progressMulti.addBarHoverText(list, 0)); - builder.widget(progressBar); - - progressBar = new ProgressWidget( - () -> progressMulti.getFillPercentage(1), - 100, 115, 94, 7, - progressMulti.getProgressBarTexture(1), ProgressWidget.MoveType.HORIZONTAL) - .setHoverTextConsumer(list -> progressMulti.addBarHoverText(list, 1)); - builder.widget(progressBar); - } else { - // single bar - ProgressWidget progressBar = new ProgressWidget( - () -> progressMulti.getFillPercentage(0), - 4, 115, 190, 7, - progressMulti.getProgressBarTexture(0), ProgressWidget.MoveType.HORIZONTAL) - .setHoverTextConsumer(list -> progressMulti.addBarHoverText(list, 0)); - builder.widget(progressBar); - } - builder.widget(new IndicatorImageWidget(174, 93, 17, 17, getLogo()) - .setWarningStatus(getWarningLogo(), this::addWarningText) - .setErrorStatus(getErrorLogo(), this::addErrorText)); - } else { - builder.image(4, 4, 190, 117, GuiTextures.DISPLAY); - builder.widget(new IndicatorImageWidget(174, 101, 17, 17, getLogo()) - .setWarningStatus(getWarningLogo(), this::addWarningText) - .setErrorStatus(getErrorLogo(), this::addErrorText)); - } - - builder.label(9, 9, getMetaFullName(), 0xFFFFFF); - builder.widget(new AdvancedTextWidget(9, 20, this::addDisplayText, 0xFFFFFF) - .setMaxWidthLimit(181) - .setClickHandler(this::handleDisplayClick)); - - // Power Button - // todo in the future, refactor so that this class is instanceof IControllable. - IControllable controllable = getCapability(GregtechTileCapabilities.CAPABILITY_CONTROLLABLE, null); - if (controllable != null) { - builder.widget(new ImageCycleButtonWidget(173, 183, 18, 18, GuiTextures.BUTTON_POWER, - controllable::isWorkingEnabled, controllable::setWorkingEnabled)); - builder.widget(new ImageWidget(173, 201, 18, 6, GuiTextures.BUTTON_POWER_DETAIL)); - } - - // Voiding Mode Button - if (shouldShowVoidingModeButton()) { - builder.widget(new ImageCycleButtonWidget(173, 161, 18, 18, GuiTextures.BUTTON_VOID_MULTIBLOCK, - 4, this::getVoidingMode, this::setVoidingMode) - .setTooltipHoverString(MultiblockWithDisplayBase::getVoidingModeTooltip)); - } else { - builder.widget(new ImageWidget(173, 161, 18, 18, GuiTextures.BUTTON_VOID_NONE) - .setTooltip("gregtech.gui.multiblock_voiding_not_supported")); - } - - // Distinct Buses Button - if (this instanceof IDistinctBusController distinct && distinct.canBeDistinct()) { - builder.widget(new ImageCycleButtonWidget(173, 143, 18, 18, GuiTextures.BUTTON_DISTINCT_BUSES, - distinct::isDistinct, distinct::setDistinct) - .setTooltipHoverString(i -> "gregtech.multiblock.universal.distinct_" + - (i == 0 ? "disabled" : "enabled"))); - } else { - builder.widget(new ImageWidget(173, 143, 18, 18, GuiTextures.BUTTON_NO_DISTINCT_BUSES) - .setTooltip("gregtech.multiblock.universal.distinct_not_supported")); - } - - // Flex Button - builder.widget(getFlexButton(173, 125, 18, 18)); - - builder.bindPlayerInventory(entityPlayer.inventory, 125); - return builder; - } - - /** - * Add a custom third button to the Multiblock UI. By default, this is a placeholder - * stating that there is no additional functionality for this Multiblock. - *
- *
- * Parameters should be passed directly to the created widget. Size will be 18x18. - */ - @SuppressWarnings("SameParameterValue") - @NotNull - protected Widget getFlexButton(int x, int y, int width, int height) { - return new ImageWidget(x, y, width, height, GuiTextures.BUTTON_NO_FLEX) - .setTooltip("gregtech.multiblock.universal.no_flex_button"); - } - - protected @NotNull TextureArea getLogo() { - return GuiTextures.GREGTECH_LOGO_DARK; - } - - protected @NotNull TextureArea getWarningLogo() { - return GuiTextures.GREGTECH_LOGO_BLINKING_YELLOW; - } - - protected @NotNull TextureArea getErrorLogo() { - return GuiTextures.GREGTECH_LOGO_BLINKING_RED; - } - - /** - * Returns a list of text indicating any current warnings in this Multiblock. - * Recommended to only display warnings if the structure is already formed. - */ - protected void addWarningText(List textList) { - MultiblockDisplayText.builder(textList, isStructureFormed(), false) - .addMaintenanceProblemLines(getMaintenanceProblems()); - } - - /** - * Returns a list of translation keys indicating any current errors in this Multiblock. - * Prioritized over any warnings provided by {@link MultiblockWithDisplayBase#addWarningText}. - */ - protected void addErrorText(List textList) { - MultiblockDisplayText.builder(textList, isStructureFormed()) - .addMufflerObstructedLine(hasMufflerMechanics() && !isMufflerFaceFree()); - } - - protected boolean shouldShowVoidingModeButton() { + public boolean shouldShowVoidingModeButton() { return true; } - protected int getVoidingMode() { + public final int getVoidingMode() { return voidingMode.ordinal(); } - protected void setVoidingMode(int mode) { + public final void setVoidingMode(int mode) { this.voidingMode = VoidingMode.VALUES[mode]; this.voidingFluids = mode >= 2; @@ -533,13 +362,41 @@ protected void setVoidingMode(int mode) { markDirty(); } - protected static String getVoidingModeTooltip(int mode) { + public @NotNull String getVoidingModeTooltip(int mode) { return VoidingMode.VALUES[mode].getName(); } @Override - protected ModularUI createUI(EntityPlayer entityPlayer) { - return createUITemplate(entityPlayer).build(getHolder(), entityPlayer); + public boolean usesMui2() { + return true; + } + + protected void configureDisplayText(MultiblockUIBuilder builder) {} + + protected void configureErrorText(MultiblockUIBuilder builder) { + builder.structureFormed(isStructureFormed()); + if (hasMufflerMechanics()) + builder.addMufflerObstructedLine(!isMufflerFaceFree()); + if (hasMaintenanceMechanics()) + builder.addMaintenanceProblemLines(getMaintenanceProblems(), false); + } + + protected void configureWarningText(MultiblockUIBuilder builder) { + if (hasMaintenanceMechanics()) + builder.addMaintenanceProblemLines(getMaintenanceProblems(), true); + } + + protected MultiblockUIFactory createUIFactory() { + return new MultiblockUIFactory(this) + .configureDisplayText(this::configureDisplayText) + .configureWarningText(this::configureWarningText) + .configureErrorText(this::configureErrorText); + } + + @Override + public final ModularPanel buildUI(PosGuiData guiData, PanelSyncManager panelSyncManager) { + if (uiFactory == null) uiFactory = createUIFactory(); + return this.uiFactory.buildUI(guiData, panelSyncManager); } @Override diff --git a/src/main/java/gregtech/api/metatileentity/multiblock/ProgressBarMultiblock.java b/src/main/java/gregtech/api/metatileentity/multiblock/ProgressBarMultiblock.java new file mode 100644 index 00000000000..e8046b903b4 --- /dev/null +++ b/src/main/java/gregtech/api/metatileentity/multiblock/ProgressBarMultiblock.java @@ -0,0 +1,21 @@ +package gregtech.api.metatileentity.multiblock; + +import gregtech.api.metatileentity.multiblock.ui.TemplateBarBuilder; + +import com.cleanroommc.modularui.value.sync.PanelSyncManager; + +import java.util.List; +import java.util.function.UnaryOperator; + +public interface ProgressBarMultiblock { + + int getProgressBarCount(); + + // the bar only needs three things + // progress, texture, and tooltip + void registerBars(List> bars, PanelSyncManager syncManager); + + default boolean hasBars() { + return getProgressBarCount() > 0; + } +} diff --git a/src/main/java/gregtech/api/metatileentity/multiblock/RecipeMapMultiblockController.java b/src/main/java/gregtech/api/metatileentity/multiblock/RecipeMapMultiblockController.java index 02ba5655857..cebe13bc959 100644 --- a/src/main/java/gregtech/api/metatileentity/multiblock/RecipeMapMultiblockController.java +++ b/src/main/java/gregtech/api/metatileentity/multiblock/RecipeMapMultiblockController.java @@ -1,6 +1,7 @@ package gregtech.api.metatileentity.multiblock; import gregtech.api.GTValues; +import gregtech.api.capability.IControllable; import gregtech.api.capability.IDistinctBusController; import gregtech.api.capability.IEnergyContainer; import gregtech.api.capability.IMultipleTankHandler; @@ -10,6 +11,7 @@ import gregtech.api.capability.impl.MultiblockRecipeLogic; import gregtech.api.items.itemhandlers.GTItemStackHandler; import gregtech.api.metatileentity.IDataInfoProvider; +import gregtech.api.metatileentity.multiblock.ui.MultiblockUIBuilder; import gregtech.api.pattern.PatternMatchContext; import gregtech.api.pattern.TraceabilityPredicate; import gregtech.api.recipes.Recipe; @@ -40,7 +42,8 @@ import java.util.List; public abstract class RecipeMapMultiblockController extends MultiblockWithDisplayBase implements IDataInfoProvider, - ICleanroomReceiver, IDistinctBusController { + ICleanroomReceiver, IDistinctBusController, + IControllable { public final RecipeMap recipeMap; protected MultiblockRecipeLogic recipeMapWorkable; @@ -169,22 +172,19 @@ protected boolean allowSameFluidFillForOutputs() { return true; } - @Override - protected void addDisplayText(List textList) { - MultiblockDisplayText.builder(textList, isStructureFormed()) - .setWorkingStatus(recipeMapWorkable.isWorkingEnabled(), recipeMapWorkable.isActive()) - .addEnergyUsageLine(recipeMapWorkable.getEnergyContainer()) + protected void configureDisplayText(MultiblockUIBuilder builder) { + builder.setWorkingStatus(recipeMapWorkable.isWorkingEnabled(), recipeMapWorkable.isActive()) + .addEnergyUsageLine(this.getEnergyContainer()) .addEnergyTierLine(GTUtility.getTierByVoltage(recipeMapWorkable.getMaxVoltage())) .addParallelsLine(recipeMapWorkable.getParallelLimit()) .addWorkingStatusLine() - .addProgressLine(recipeMapWorkable.getProgressPercent()); + .addProgressLine(recipeMapWorkable.getProgress(), recipeMapWorkable.getMaxProgress()) + .addRecipeOutputLine(recipeMapWorkable); } - @Override - protected void addWarningText(List textList) { - MultiblockDisplayText.builder(textList, isStructureFormed(), false) - .addLowPowerLine(recipeMapWorkable.isHasNotEnoughEnergy()) - .addMaintenanceProblemLines(getMaintenanceProblems()); + protected void configureWarningText(MultiblockUIBuilder builder) { + builder.addLowPowerLine(recipeMapWorkable.isHasNotEnoughEnergy()); + super.configureWarningText(builder); } @Override @@ -357,4 +357,14 @@ public void setCleanroom(@NotNull ICleanroomProvider provider) { public void unsetCleanroom() { this.cleanroom = null; } + + @Override + public boolean isWorkingEnabled() { + return recipeMapWorkable.isWorkingEnabled(); + } + + @Override + public void setWorkingEnabled(boolean isWorkingAllowed) { + recipeMapWorkable.setWorkingEnabled(isWorkingAllowed); + } } diff --git a/src/main/java/gregtech/api/metatileentity/multiblock/RecipeMapSteamMultiblockController.java b/src/main/java/gregtech/api/metatileentity/multiblock/RecipeMapSteamMultiblockController.java index fd396609419..dd7c0b5f039 100644 --- a/src/main/java/gregtech/api/metatileentity/multiblock/RecipeMapSteamMultiblockController.java +++ b/src/main/java/gregtech/api/metatileentity/multiblock/RecipeMapSteamMultiblockController.java @@ -1,27 +1,23 @@ package gregtech.api.metatileentity.multiblock; +import gregtech.api.capability.IControllable; import gregtech.api.capability.IMultipleTankHandler; import gregtech.api.capability.impl.FluidTankList; import gregtech.api.capability.impl.ItemHandlerList; import gregtech.api.capability.impl.SteamMultiblockRecipeLogic; -import gregtech.api.gui.GuiTextures; -import gregtech.api.gui.ModularUI; -import gregtech.api.gui.widgets.AdvancedTextWidget; -import gregtech.api.gui.widgets.IndicatorImageWidget; import gregtech.api.items.itemhandlers.GTItemStackHandler; import gregtech.api.metatileentity.MTETrait; +import gregtech.api.metatileentity.multiblock.ui.MultiblockUIBuilder; +import gregtech.api.mui.GTGuiTheme; import gregtech.api.pattern.PatternMatchContext; import gregtech.api.pattern.TraceabilityPredicate; import gregtech.api.recipes.Recipe; import gregtech.api.recipes.RecipeMap; -import gregtech.api.util.TextComponentUtil; -import gregtech.api.util.TextFormattingUtil; +import gregtech.api.util.KeyUtil; import gregtech.common.ConfigHolder; -import net.minecraft.entity.player.EntityPlayer; import net.minecraft.util.ResourceLocation; import net.minecraft.util.SoundEvent; -import net.minecraft.util.text.ITextComponent; import net.minecraft.util.text.TextFormatting; import net.minecraftforge.fluids.IFluidTank; import net.minecraftforge.items.IItemHandlerModifiable; @@ -29,10 +25,9 @@ import codechicken.lib.render.CCRenderState; import codechicken.lib.render.pipeline.IVertexOperation; import codechicken.lib.vec.Matrix4; +import com.cleanroommc.modularui.api.drawable.IKey; -import java.util.List; - -public abstract class RecipeMapSteamMultiblockController extends MultiblockWithDisplayBase { +public abstract class RecipeMapSteamMultiblockController extends MultiblockWithDisplayBase implements IControllable { protected static final double CONVERSION_RATE = ConfigHolder.machines.multiblockSteamToEU; @@ -101,42 +96,42 @@ private void resetTileAbilities() { } @Override - protected void addDisplayText(List textList) { - MultiblockDisplayText.builder(textList, isStructureFormed()) - .setWorkingStatus(recipeMapWorkable.isWorkingEnabled(), recipeMapWorkable.isActive()) - .addCustom(tl -> { + protected void configureDisplayText(MultiblockUIBuilder builder) { + builder.setWorkingStatus(recipeMapWorkable.isWorkingEnabled(), recipeMapWorkable.isActive()) + .addCustom((keyManager, syncer) -> { // custom steam tank line IFluidTank steamFluidTank = recipeMapWorkable.getSteamFluidTankCombined(); - if (steamFluidTank != null && steamFluidTank.getCapacity() > 0) { - String stored = TextFormattingUtil.formatNumbers(steamFluidTank.getFluidAmount()); - String capacity = TextFormattingUtil.formatNumbers(steamFluidTank.getCapacity()); - - ITextComponent steamInfo = TextComponentUtil.stringWithColor( - TextFormatting.BLUE, - stored + " / " + capacity + " L"); - - tl.add(TextComponentUtil.translationWithColor( - TextFormatting.GRAY, - "gregtech.multiblock.steam.steam_stored", - steamInfo)); + int stored = syncer.syncInt(steamFluidTank.getFluidAmount()); + int capacity = syncer.syncInt(steamFluidTank.getCapacity()); + if (capacity > 0) { + IKey steamInfo = KeyUtil.string(TextFormatting.BLUE, "%s/%s L", + KeyUtil.number(stored), + KeyUtil.number(capacity)); + IKey steamStored = KeyUtil.lang(TextFormatting.GRAY, + "gregtech.multiblock.steam.steam_stored", steamInfo); + keyManager.add(steamStored); } }) .addParallelsLine(recipeMapWorkable.getParallelLimit()) .addWorkingStatusLine() - .addProgressLine(recipeMapWorkable.getProgressPercent()); + .addProgressLine(recipeMapWorkable.getProgress(), recipeMapWorkable.getMaxProgress()) + .addRecipeOutputLine(recipeMapWorkable); } @Override - protected void addWarningText(List textList) { - MultiblockDisplayText.builder(textList, isStructureFormed(), false) - .addCustom(tl -> { - if (isStructureFormed() && recipeMapWorkable.isHasNotEnoughEnergy()) { - tl.add(TextComponentUtil.translationWithColor( - TextFormatting.YELLOW, - "gregtech.multiblock.steam.low_steam")); - } - }) - .addMaintenanceProblemLines(getMaintenanceProblems()); + protected void configureWarningText(MultiblockUIBuilder builder) { + builder.addCustom((list, syncer) -> { + boolean noEnergy = syncer.syncBoolean(recipeMapWorkable.isHasNotEnoughEnergy()); + if (isStructureFormed() && noEnergy) { + list.add(KeyUtil.lang(TextFormatting.YELLOW, "gregtech.multiblock.steam.low_steam")); + } + }); + super.configureWarningText(builder); + } + + @Override + public GTGuiTheme getUITheme() { + return GTGuiTheme.BRONZE; } @Override @@ -188,20 +183,12 @@ public boolean isActive() { } @Override - protected ModularUI.Builder createUITemplate(EntityPlayer entityPlayer) { - ModularUI.Builder builder = ModularUI - .builder(GuiTextures.BACKGROUND_STEAM.get(ConfigHolder.machines.steelSteamMultiblocks), 176, 208); - builder.shouldColor(false); - builder.image(4, 4, 168, 117, GuiTextures.DISPLAY_STEAM.get(ConfigHolder.machines.steelSteamMultiblocks)); - builder.label(9, 9, getMetaFullName(), 0xFFFFFF); - builder.widget(new AdvancedTextWidget(9, 20, this::addDisplayText, 0xFFFFFF) - .setMaxWidthLimit(162) - .setClickHandler(this::handleDisplayClick)); - builder.widget(new IndicatorImageWidget(152, 101, 17, 17, getLogo()) - .setWarningStatus(getWarningLogo(), this::addWarningText) - .setErrorStatus(getErrorLogo(), this::addErrorText)); - builder.bindPlayerInventory(entityPlayer.inventory, - GuiTextures.SLOT_STEAM.get(ConfigHolder.machines.steelSteamMultiblocks), 7, 125); - return builder; + public boolean isWorkingEnabled() { + return recipeMapWorkable.isWorkingEnabled(); + } + + @Override + public void setWorkingEnabled(boolean isWorkingAllowed) { + recipeMapWorkable.setWorkingEnabled(isWorkingAllowed); } } diff --git a/src/main/java/gregtech/api/metatileentity/multiblock/ui/KeyManager.java b/src/main/java/gregtech/api/metatileentity/multiblock/ui/KeyManager.java new file mode 100644 index 00000000000..3ebb926f662 --- /dev/null +++ b/src/main/java/gregtech/api/metatileentity/multiblock/ui/KeyManager.java @@ -0,0 +1,17 @@ +package gregtech.api.metatileentity.multiblock.ui; + +import com.cleanroommc.modularui.api.drawable.IDrawable; + +@FunctionalInterface +public interface KeyManager { + + void add(Operation op); + + default void add(IDrawable drawable) { + add(Operation.addLineSpace(drawable)); + } + + default void addAll(Iterable drawables) { + drawables.forEach(this::add); + } +} diff --git a/src/main/java/gregtech/api/metatileentity/multiblock/ui/MultiblockUIBuilder.java b/src/main/java/gregtech/api/metatileentity/multiblock/ui/MultiblockUIBuilder.java new file mode 100644 index 00000000000..2dd58a00df4 --- /dev/null +++ b/src/main/java/gregtech/api/metatileentity/multiblock/ui/MultiblockUIBuilder.java @@ -0,0 +1,1051 @@ +package gregtech.api.metatileentity.multiblock.ui; + +import gregtech.api.GTValues; +import gregtech.api.capability.IEnergyContainer; +import gregtech.api.capability.impl.AbstractRecipeLogic; +import gregtech.api.capability.impl.ComputationRecipeLogic; +import gregtech.api.metatileentity.MetaTileEntity; +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.output.impl.ChancedFluidOutput; +import gregtech.api.recipes.chance.output.impl.ChancedItemOutput; +import gregtech.api.unification.material.Materials; +import gregtech.api.util.GTHashMaps; +import gregtech.api.util.GTUtility; +import gregtech.api.util.KeyUtil; +import gregtech.api.util.TextFormattingUtil; +import gregtech.api.util.function.ByteSupplier; +import gregtech.api.util.function.FloatSupplier; +import gregtech.common.ConfigHolder; +import gregtech.common.items.ToolItems; + +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.drawable.ItemDrawable; +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.Object2IntMap; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import java.io.IOException; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collection; +import java.util.List; +import java.util.Objects; +import java.util.function.BiConsumer; +import java.util.function.BooleanSupplier; +import java.util.function.Consumer; +import java.util.function.DoubleSupplier; +import java.util.function.Function; +import java.util.function.IntSupplier; +import java.util.function.LongSupplier; +import java.util.function.Supplier; + +@SuppressWarnings({ "UnusedReturnValue", "unused" }) +public class MultiblockUIBuilder { + + private final List operations = new ArrayList<>(); + + private Consumer action; + private final InternalSyncHandler syncHandler = new InternalSyncHandler(); + + private static final int DEFAULT_MAX_RECIPE_LINES = 25; + + @Nullable + private InternalSyncer syncer; + + private boolean isWorkingEnabled; + private boolean isActive; + private boolean isStructureFormed; + + // Keys for the three-state working system, can be set custom by multiblocks. + private IKey idlingKey = IKey.lang("gregtech.multiblock.idling").style(TextFormatting.GRAY); + private IKey pausedKey = IKey.lang("gregtech.multiblock.work_paused").style(TextFormatting.GOLD); + private IKey runningKey = IKey.lang("gregtech.multiblock.running").style(TextFormatting.GREEN); + private boolean dirty; + private Runnable onRebuild; + + @NotNull + InternalSyncer getSyncer() { + if (this.syncer == null) { + this.syncer = new InternalSyncer(isServer()); + } + return this.syncer; + } + + void updateFormed(boolean isStructureFormed) { + this.isStructureFormed = getSyncer().syncBoolean(isStructureFormed); + } + + private boolean isServer() { + return !this.syncHandler.getSyncManager().isClient(); + } + + public MultiblockUIBuilder structureFormed(boolean structureFormed) { + updateFormed(structureFormed); + if (!this.isStructureFormed) { + var base = KeyUtil.lang(TextFormatting.RED, "gregtech.multiblock.invalid_structure"); + var hover = KeyUtil.lang(TextFormatting.GRAY, + "gregtech.multiblock.invalid_structure.tooltip"); + addHoverableKey(base, hover); + } + return this; + } + + public MultiblockUIBuilder title(String lang) { + addKey(KeyUtil.lang(TextFormatting.WHITE, lang)); + return this; + } + + /** Set the current working enabled and active status of this multiblock, used by many line addition calls. */ + public MultiblockUIBuilder setWorkingStatus(boolean isWorkingEnabled, boolean isActive) { + this.isWorkingEnabled = this.getSyncer().syncBoolean(isWorkingEnabled); + this.isActive = this.getSyncer().syncBoolean(isActive); + return this; + } + + /** + * Set custom translation keys for the three-state "Idling", "Paused", "Running" display text. + * You still must call {@link MultiblockUIBuilder#addWorkingStatusLine()} for these to appear! + *
+ * Pass any key as null for it to continue to use the default key. + * + * @param idlingKey The translation key for the Idle state, or "!isActive && isWorkingEnabled". + * @param pausedKey The translation key for the Paused state, or "!isWorkingEnabled". + * @param runningKey The translation key for the Running state, or "isActive". + */ + public MultiblockUIBuilder setWorkingStatusKeys(String idlingKey, String pausedKey, String runningKey) { + if (idlingKey != null) this.idlingKey = IKey.lang(idlingKey).style(TextFormatting.GRAY); + if (pausedKey != null) this.pausedKey = IKey.lang(pausedKey).style(TextFormatting.GOLD); + if (runningKey != null) this.runningKey = IKey.lang(runningKey).style(TextFormatting.GREEN); + return this; + } + + /** + * Adds the max EU/t that this multiblock can use. + *
+ * Added if the structure is formed and if the passed energy container has greater than zero capacity. + */ + @SuppressWarnings("Convert2MethodRef") + public MultiblockUIBuilder addEnergyUsageLine(IEnergyContainer energyContainer) { + if (!isStructureFormed) return this; + + // cannot use method reference since energy container can be null on client + long capacity = getSyncer().syncLong(() -> energyContainer.getEnergyCapacity()); + long inV = getSyncer().syncLong(() -> energyContainer.getInputVoltage()); + long outV = getSyncer().syncLong(() -> energyContainer.getOutputVoltage()); + + if (capacity <= 0) return this; + + long maxVoltage = Math.max(inV, outV); + int tier = GTUtility.getFloorTierByVoltage(maxVoltage); + + IKey bodyText = KeyUtil.lang(TextFormatting.GRAY, + "gregtech.multiblock.max_energy_per_tick", + KeyUtil.number(maxVoltage), + KeyUtil.string(GTValues.VOCNF[tier])); + + var hoverText = KeyUtil.lang(TextFormatting.GRAY, + "gregtech.multiblock.max_energy_per_tick_hover"); + + addHoverableKey(bodyText, hoverText); + return this; + } + + /** + * Adds the max Recipe Tier that this multiblock can use for recipe lookup. + *
+ * Added if the structure is formed and if the passed tier is a valid energy tier index for {@link GTValues#VNF}. + */ + public MultiblockUIBuilder addEnergyTierLine(int tier) { + if (!isStructureFormed) return this; + tier = getSyncer().syncInt(tier); + if (tier < GTValues.ULV || tier > GTValues.MAX_TRUE) return this; + + var bodyText = KeyUtil.lang(TextFormatting.GRAY, + "gregtech.multiblock.max_recipe_tier", GTValues.VOCNF[tier]); + var hoverText = KeyUtil.lang(TextFormatting.GRAY, + "gregtech.multiblock.max_recipe_tier_hover"); + addHoverableKey(bodyText, hoverText); + return this; + } + + /** + * Adds the exact EU/t that this multiblock needs to run. + *
+ * Added if the structure is formed and if the passed value is greater than zero. + */ + public MultiblockUIBuilder addEnergyUsageExactLine(long energyUsage) { + if (!isStructureFormed) return this; + energyUsage = getSyncer().syncLong(energyUsage); + if (energyUsage > 0) { + String energyFormatted = TextFormattingUtil.formatNumbers(energyUsage); + // wrap in text component to keep it from being formatted + int tier = GTUtility.getOCTierByVoltage(energyUsage); + var voltageName = KeyUtil.string(GTValues.VOCNF[tier]); + + addKey(KeyUtil.lang(TextFormatting.GRAY, + "gregtech.multiblock.energy_consumption", energyFormatted, voltageName)); + } + return this; + } + + /** + * Adds the max EU/t that this multiblock can produce. + *
+ * Added if the structure is formed and if the max voltage is not zero and greater than the recipe EU/t. + */ + public MultiblockUIBuilder addEnergyProductionLine(long maxVoltage, long recipeEUt) { + if (!isStructureFormed) return this; + maxVoltage = getSyncer().syncLong(maxVoltage); + recipeEUt = getSyncer().syncLong(recipeEUt); + if (maxVoltage != 0 && maxVoltage >= recipeEUt) { + String energyFormatted = TextFormattingUtil.formatNumbers(maxVoltage); + // wrap in text component to keep it from being formatted + int tier = GTUtility.getFloorTierByVoltage(maxVoltage); + var voltageName = KeyUtil.string(GTValues.VOCNF[tier]); + + addKey(KeyUtil.lang(TextFormatting.GRAY, + "gregtech.multiblock.max_energy_per_tick", energyFormatted, voltageName)); + } + return this; + } + + /** + * Adds the max EU/t that this multiblock can produce, including how many amps. Recommended for multi-amp outputting + * multis. + *
+ * Added if the structure is formed, if the amperage is greater than zero and if the max voltage is greater than + * zero. + */ + public MultiblockUIBuilder addEnergyProductionAmpsLine(long maxVoltage, int amperage) { + if (!isStructureFormed) return this; + maxVoltage = getSyncer().syncLong(maxVoltage); + amperage = getSyncer().syncInt(amperage); + if (maxVoltage != 0 && amperage != 0) { + String energyFormatted = TextFormattingUtil.formatNumbers(maxVoltage); + // wrap in text component to keep it from being formatted + int tier = GTUtility.getFloorTierByVoltage(maxVoltage); + var voltageName = KeyUtil.string(GTValues.VOCNF[tier]); + + addKey(KeyUtil.lang(TextFormatting.GRAY, + "gregtech.multiblock.max_energy_per_tick_amps", + energyFormatted, amperage, voltageName)); + } + return this; + } + + /** + * Adds the max CWU/t that this multiblock can use. + *
+ * Added if the structure is formed and if the max CWU/t is greater than zero. + */ + public MultiblockUIBuilder addComputationUsageLine(int maxCWUt) { + if (!isStructureFormed) return this; + maxCWUt = getSyncer().syncInt(maxCWUt); + if (maxCWUt > 0) { + var computation = KeyUtil.number(TextFormatting.AQUA, maxCWUt); + addKey(KeyUtil.lang(TextFormatting.GRAY, + "gregtech.multiblock.computation.max", computation)); + } + return this; + } + + /** + * Adds a currently used CWU/t line. + *
+ * Added if the structure is formed, the machine is active, and the current CWU/t is greater than zero. + */ + public MultiblockUIBuilder addComputationUsageExactLine(int currentCWUt) { + if (!isStructureFormed) return this; + currentCWUt = getSyncer().syncInt(currentCWUt); + if (isActive && currentCWUt > 0) { + var computation = KeyUtil.number(TextFormatting.AQUA, currentCWUt, " CWU/t"); + addKey(KeyUtil.lang(TextFormatting.GRAY, + "gregtech.multiblock.computation.usage", computation)); + } + return this; + } + + /** + * Adds a three-state indicator line, showing if the machine is running, paused, or idling. + *
+ * Added if the structure is formed. + */ + public MultiblockUIBuilder addWorkingStatusLine() { + if (!isStructureFormed) return this; + + if (!isWorkingEnabled) { + addKey(pausedKey); + } else if (isActive) { + addKey(runningKey); + } else { + addKey(idlingKey); + } + return this; + } + + /** + * Adds the "Work Paused." line. + *
+ * Added if working is not enabled, or if the checkState passed parameter is false. Also added only if formed. + */ + public MultiblockUIBuilder addWorkPausedLine(boolean checkState) { + if (!isStructureFormed) return this; + if (!checkState || !isWorkingEnabled) { + addKey(pausedKey); + } + return this; + } + + /** + * Adds the "Running Perfectly." line. + *
+ * Added if machine is active, or if the checkState passed parameter is false. Also added only if formed. + */ + public MultiblockUIBuilder addRunningPerfectlyLine(boolean checkState) { + if (!isStructureFormed) return this; + if (!checkState || isActive) { + addKey(runningKey); + } + return this; + } + + /** + * Adds the "Idling." line. + *
+ * Added if the machine is not active and working is enabled, or if the checkState passed parameter is false. Also + * added only if formed. + */ + public MultiblockUIBuilder addIdlingLine(boolean checkState) { + if (!isStructureFormed) return this; + if (!checkState || (isWorkingEnabled && !isActive)) { + addKey(idlingKey); + } + return this; + } + + /** + * Adds a progress line that displays recipe progress as "time / total time (percentage)". + *
+ * Added if structure is formed and the machine is active. + * + * @param progress current progress. + * @param maxProgress total progress to be made. + */ + public MultiblockUIBuilder addProgressLine(int progress, int maxProgress) { + if (!isStructureFormed || !isActive) return this; + + progress = getSyncer().syncInt(progress); + maxProgress = getSyncer().syncInt(maxProgress); + + if (maxProgress <= 20) { + addKey(KeyUtil.lang(TextFormatting.GRAY, "gregtech.multiblock.recipe_progress.ticks", + // %02d is not supported by lang + String.format("%02d", progress), String.format("%02d", maxProgress), + (float) progress / maxProgress * 100f)); + } else { + addKey(KeyUtil.lang(TextFormatting.GRAY, "gregtech.multiblock.recipe_progress.seconds", + progress / 20f, maxProgress / 20f, (float) progress / maxProgress * 100f)); + } + + return this; + } + + /** + * Adds a progress line that displays recipe progress as "time / total time (percentage)". + *
+ * Added if structure is formed and the machine is active. + * + */ + public MultiblockUIBuilder addComputationProgressLine(ComputationRecipeLogic crl) { + if (!isStructureFormed || !isActive) return this; + + int progress = getSyncer().syncInt(crl.getProgress()); + int maxProgress = getSyncer().syncInt(crl.getMaxProgress()); + int maxCwu = getSyncer().syncInt(() -> crl.getComputationProvider().getMaxCWUt()); + + if (crl.shouldShowDuration()) { + addKey(IKey.str("%s / %s CWU", KeyUtil.number(progress), KeyUtil.number(maxProgress)) + .style(TextFormatting.GRAY)); + } else { + // do fancy things + int cwuRate = getSyncer().syncInt(crl.getCurrentDrawnCWUt()); + int currentCwu = progress * cwuRate; + } + 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. + */ + public MultiblockUIBuilder addParallelsLine(int numParallels) { + if (!isStructureFormed) return this; + numParallels = getSyncer().syncInt(numParallels); + if (numParallels > 1) { + addKey(KeyUtil.lang(TextFormatting.GRAY, + "gregtech.multiblock.parallel", + KeyUtil.number(TextFormatting.DARK_PURPLE, numParallels))); + } + return this; + } + + /** + * Adds a warning line when the machine is low on power. + *
+ * Added if the structure is formed and if the supplier returns true. + */ + public MultiblockUIBuilder addLowPowerLine(@NotNull BooleanSupplier isLowPower) { + if (!isStructureFormed) return this; + if (getSyncer().syncBoolean(isLowPower)) { + addKey(KeyUtil.lang(TextFormatting.YELLOW, "gregtech.multiblock.not_enough_energy")); + } + return this; + } + + /** + * Adds a warning line when the machine is low on power. + *
+ * Added if the structure is formed and if the passed parameter is true. + */ + public MultiblockUIBuilder addLowPowerLine(boolean isLowPower) { + return addLowPowerLine(() -> isLowPower); + } + + /** + * Adds a warning line when the machine is low on computation. + *
+ * Added if the structure is formed and if the passed parameter is true. + */ + public MultiblockUIBuilder addLowComputationLine(boolean isLowComputation) { + if (!isStructureFormed) return this; + if (getSyncer().syncBoolean(isLowComputation)) { + addKey(KeyUtil.lang(TextFormatting.YELLOW, + "gregtech.multiblock.computation.not_enough_computation")); + } + return this; + } + + /** + * Adds a warning line when the machine's dynamo tier is too low for current conditions. + *
+ * Added if the structure is formed and if the passed parameter is true. + */ + public MultiblockUIBuilder addLowDynamoTierLine(boolean isTooLow) { + if (!isStructureFormed) return this; + if (getSyncer().syncBoolean(isTooLow)) { + addKey(KeyUtil.lang(TextFormatting.YELLOW, + "gregtech.multiblock.not_enough_energy_output")); + } + return this; + } + + /** + * Adds warning line(s) when the machine has maintenance problems. + *
+ * Added if there are any maintenance problems, one line per problem as well as a header.
+ * Will check the config + * setting for if maintenance is enabled automatically. + */ + public MultiblockUIBuilder addMaintenanceProblemLines(byte maintenanceProblems, boolean warning) { + if (!isStructureFormed || !ConfigHolder.machines.enableMaintenance) return this; + maintenanceProblems = getSyncer().syncByte(maintenanceProblems); + + if (warning && maintenanceProblems < 0b111111 && maintenanceProblems > 0b000000 || + !warning && maintenanceProblems == 0b000000) { + addKey(KeyUtil.lang(TextFormatting.YELLOW, + "gregtech.multiblock.universal.has_problems")); + + // Wrench + if ((maintenanceProblems & 1) == 0) { + addOperation(richText -> richText + .add(new ItemDrawable(ToolItems.WRENCH.get(Materials.Iron))) + .add(IKey.SPACE) + .add(KeyUtil.lang(TextFormatting.YELLOW, + "gregtech.multiblock.universal.problem.wrench")) + .newLine()); + } + + // Screwdriver + if (((maintenanceProblems >> 1) & 1) == 0) { + addOperation(richText -> richText + .add(new ItemDrawable(ToolItems.SCREWDRIVER.get(Materials.Iron))) + .add(IKey.SPACE) + .add(KeyUtil.lang(TextFormatting.YELLOW, + "gregtech.multiblock.universal.problem.screwdriver")) + .newLine()); + } + + // Soft Mallet + if (((maintenanceProblems >> 2) & 1) == 0) { + addOperation(richText -> richText + .add(new ItemDrawable(ToolItems.SOFT_MALLET.get(Materials.Wood))) + .add(IKey.SPACE) + .add(KeyUtil.lang(TextFormatting.YELLOW, + "gregtech.multiblock.universal.problem.soft_mallet")) + .newLine()); + } + + // Hammer + if (((maintenanceProblems >> 3) & 1) == 0) { + addOperation(richText -> richText + .add(new ItemDrawable(ToolItems.HARD_HAMMER.get(Materials.Iron))) + .add(IKey.SPACE) + .add(KeyUtil.lang(TextFormatting.YELLOW, + "gregtech.multiblock.universal.problem.hard_hammer")) + .newLine()); + } + + // Wire Cutters + if (((maintenanceProblems >> 4) & 1) == 0) { + addOperation(richText -> richText + .add(new ItemDrawable(ToolItems.WIRE_CUTTER.get(Materials.Iron))) + .add(IKey.SPACE) + .add(KeyUtil.lang(TextFormatting.YELLOW, + "gregtech.multiblock.universal.problem.wire_cutter")) + .newLine()); + } + + // Crowbar + if (((maintenanceProblems >> 5) & 1) == 0) { + addOperation(richText -> richText + .add(new ItemDrawable(ToolItems.CROWBAR.get(Materials.Iron))) + .add(IKey.SPACE) + .add(KeyUtil.lang(TextFormatting.YELLOW, + "gregtech.multiblock.universal.problem.crowbar")) + .newLine()); + } + } + return this; + } + + /** + * Adds two error lines when the machine's muffler hatch is obstructed. + *
+ * Added if the structure is formed and if the passed parameter is true. + */ + public MultiblockUIBuilder addMufflerObstructedLine(boolean isObstructed) { + if (!isStructureFormed) return this; + if (getSyncer().syncBoolean(isObstructed)) { + addKey(KeyUtil.lang(TextFormatting.RED, + "gregtech.multiblock.universal.muffler_obstructed")); + addKey(KeyUtil.lang(TextFormatting.GRAY, + "gregtech.multiblock.universal.muffler_obstructed_desc")); + } + return this; + } + + /** + * Adds a fuel consumption line showing the fuel name and the number of ticks per recipe run. + *
+ * Added if structure is formed, the machine is active, and the passed fuelName parameter is not null. + */ + public MultiblockUIBuilder addFuelNeededLine(String fuelAmount, int previousRecipeDuration) { + if (!isStructureFormed || !isActive) return this; + fuelAmount = getSyncer().syncString(fuelAmount); + previousRecipeDuration = getSyncer().syncInt(previousRecipeDuration); + addKey(KeyUtil.lang(TextFormatting.GRAY, + "gregtech.multiblock.turbine.fuel_needed", + KeyUtil.string(TextFormatting.RED, fuelAmount), + KeyUtil.number(TextFormatting.AQUA, previousRecipeDuration))); + return this; + } + + /** + * Adds the name of a recipe map to the display. + * + * @param map the {@link RecipeMap} to get the name of + */ + public MultiblockUIBuilder addRecipeMapLine(RecipeMap map) { + if (!isStructureFormed) return this; + + IKey mapName = KeyUtil.lang(TextFormatting.YELLOW, map.getTranslationKey()); + addKey(KeyUtil.lang(TextFormatting.GRAY, "gregtech.multiblock.machine_mode", mapName)); + + 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 recipe is null on first load, fix in the future + Recipe recipe = arl.getPreviousRecipe(); + + if (getSyncer().syncBoolean(recipe == null)) return this; + RecipeMap map = arl.getRecipeMap(); + if (getSyncer().syncBoolean(map == null)) return this; + + Recipe trimmed = null; + if (isServer()) { + MetaTileEntity mte = arl.getMetaTileEntity(); + trimmed = Recipe.trimRecipeOutputs(recipe, map, mte.getItemOutputLimit(), mte.getFluidOutputLimit()); + } + + int p = getSyncer().syncInt(arl.getParallelRecipesPerformed()); + if (p == 0) p = 1; + + long eut = getSyncer().syncLong(trimmed == null ? 0 : trimmed.getEUt()); + long maxVoltage = getSyncer().syncLong(arl.getMaximumOverclockVoltage()); + int maxProgress = getSyncer().syncInt(arl.getMaxProgress()); + + if (maxProgress == 0) return this; + + List itemOutputs = new ArrayList<>(); + List chancedItemOutputs = new ArrayList<>(); + List fluidOutputs = new ArrayList<>(); + List chancedFluidOutputs = new ArrayList<>(); + + if (isServer()) { + // recipe searching has to be done server only + itemOutputs.addAll(trimmed.getOutputs()); + chancedItemOutputs.addAll(trimmed.getChancedOutputs().getChancedEntries()); + fluidOutputs.addAll(trimmed.getFluidOutputs()); + chancedFluidOutputs.addAll(trimmed.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.lang(TextFormatting.GRAY, "gregtech.gui.multiblock.recipe_producing"), Operation::addLine); + + int recipeTier = GTUtility.getTierByVoltage(eut); + int machineTier = GTUtility.getOCTierByVoltage(maxVoltage); + + // items + + Object2IntMap itemMap = GTHashMaps.fromItemStackCollection(itemOutputs); + + for (var stack : itemMap.keySet()) { + addItemOutputLine(stack, (long) itemMap.getInt(stack) * p, maxProgress); + } + + for (var chancedItemOutput : chancedItemOutputs) { + // noinspection DataFlowIssue + int chance = getSyncer() + .syncInt(() -> map.chanceFunction.getBoostedChance(chancedItemOutput, recipeTier, machineTier)); + int count = chancedItemOutput.getIngredient().getCount() * p; + addChancedItemOutputLine(chancedItemOutput, count, chance, maxProgress); + } + + // fluids + + Object2IntMap fluidMap = GTHashMaps.fromFluidCollection(fluidOutputs); + + for (var stack : fluidMap.keySet()) { + addFluidOutputLine(stack, fluidMap.getInt(stack), maxProgress); + } + + for (var chancedFluidOutput : chancedFluidOutputs) { + // noinspection DataFlowIssue + int chance = getSyncer() + .syncInt(() -> map.chanceFunction.getBoostedChance(chancedFluidOutput, recipeTier, machineTier)); + int count = chancedFluidOutput.getIngredient().amount * p; + addChancedFluidOutputLine(chancedFluidOutput, count, chance, maxProgress); + } + 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); + } + + /** + * 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); + } + + /** + * Add a chanced item output of a recipe to the display. + * + * @param recipeLength max duration of the recipe + */ + private void addChancedItemOutputLine(@NotNull ChancedItemOutput output, + int count, int chance, int recipeLength) { + IKey name = KeyUtil.string(TextFormatting.AQUA, output.getIngredient().getDisplayName()); + IKey amount = KeyUtil.number(TextFormatting.GOLD, count); + IKey rate = KeyUtil.string(TextFormatting.WHITE, formatRecipeRate(getSyncer().syncInt(recipeLength), count)); + + addKey(new GTObjectDrawable(output, count) + .setBoostFunction(entry -> chance) + .asIcon() + .asHoverable() + .addTooltipLine(formatRecipeData(name, amount, rate)), Operation::add); + } + + /** + * Add a chanced fluid output of a recipe to the display. + * + * @param recipeLength max duration of the recipe + */ + private void addChancedFluidOutputLine(ChancedFluidOutput output, + int count, int chance, int recipeLength) { + IKey name = KeyUtil.fluid(TextFormatting.AQUA, output.getIngredient()); + IKey amount = KeyUtil.number(TextFormatting.GOLD, count); + IKey rate = KeyUtil.string(TextFormatting.WHITE, + formatRecipeRate(getSyncer().syncInt(recipeLength), count)); + + addKey(new GTObjectDrawable(output, count) + .setBoostFunction(entry -> chance) + .asIcon() + .asHoverable() + .addTooltipLine(formatRecipeData(name, amount, rate)), 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); + return this; + } + + /** Add custom text dynamically, allowing for custom application logic. */ + public MultiblockUIBuilder addCustom(BiConsumer customConsumer) { + customConsumer.accept(this::addOperation, getSyncer()); + return this; + } + + public boolean isEmpty() { + return this.operations.isEmpty(); + } + + public void clear() { + this.operations.clear(); + } + + protected boolean hasChanged() { + if (this.action == null) return false; + return getSyncer().hasChanged(); + } + + public void sync(String key, PanelSyncManager syncManager) { + syncManager.syncValue(key, this.syncHandler); + } + + /** + * Builds the passed in rich text with operations and drawables.
+ * Will clear and rebuild if this builder is marked dirty + * + * @param richText the rich text to add drawables to + */ + public void build(IRichTextBuilder richText) { + if (dirty) { + clear(); + onRebuild(); + runAction(); + dirty = false; + } + for (Operation op : operations) { + op.accept(richText); + } + } + + private void onRebuild() { + if (this.onRebuild != null) { + this.onRebuild.run(); + } + } + + /** + * Mark this builder as dirty. Will be rebuilt during {@link #build(IRichTextBuilder) build()} + */ + public void markDirty() { + dirty = true; + } + + /* + * this is run on the server side to write values to the internal syncer + * those values are then synced to the client and read back in the same order + */ + private void runAction() { + if (this.action != null) { + this.action.accept(this); + } + } + + /** + * Set the action for this builder. Called on server and client. + * + * @param action the action to apply to this builder + */ + public void setAction(Consumer action) { + this.action = action; + } + + /** + * The runnable is called prior to rebuilding, usually used for updating {@link #structureFormed(boolean)} + * + * @param onRebuild the runnable to run prior to rebuilding + */ + public void onRebuild(Runnable onRebuild) { + this.onRebuild = onRebuild; + } + + private void addHoverableKey(IKey key, IDrawable... hover) { + if (isServer()) return; + addKey(KeyUtil.setHover(key, hover)); + } + + private void addKey(IDrawable key) { + addOperation(Operation.addLineSpace(key)); + } + + private void addKey(IDrawable key, Function function) { + addOperation(function.apply(key)); + } + + private void addOperation(@NotNull Operation op) { + if (!isServer()) this.operations.add(op); + } + + public class InternalSyncer implements UISyncer { + + private final PacketBuffer internal = new PacketBuffer(Unpooled.buffer()); + private final boolean isServer; + + private InternalSyncer(boolean isServer) { + this.isServer = isServer; + } + + private boolean isServer() { + return this.isServer; + } + + @Override + public boolean syncBoolean(@NotNull BooleanSupplier initial) { + if (isServer()) { + boolean val = initial.getAsBoolean(); + internal.writeBoolean(val); + return val; + } else { + return internal.readBoolean(); + } + } + + @Override + public int syncInt(@NotNull IntSupplier initial) { + if (isServer()) { + int val = initial.getAsInt(); + internal.writeInt(val); + return val; + } else { + return internal.readInt(); + } + } + + @Override + public long syncLong(@NotNull LongSupplier initial) { + if (isServer()) { + long val = initial.getAsLong(); + internal.writeLong(val); + return val; + } else { + return internal.readLong(); + } + } + + @Override + public byte syncByte(@NotNull ByteSupplier initial) { + if (isServer()) { + byte val = initial.getByte(); + internal.writeByte(val); + return val; + } else { + return internal.readByte(); + } + } + + @Override + public double syncDouble(@NotNull DoubleSupplier initial) { + if (isServer()) { + double val = initial.getAsDouble(); + internal.writeDouble(val); + return val; + } else { + return internal.readDouble(); + } + } + + @Override + public float syncFloat(@NotNull FloatSupplier initial) { + if (isServer()) { + float val = initial.getFloat(); + internal.writeFloat(val); + return val; + } else { + return internal.readFloat(); + } + } + + @Override + @NotNull + public T syncObject(@NotNull Supplier initial, IByteBufSerializer serializer, + IByteBufDeserializer deserializer) { + if (isServer()) { + T val = initial.get(); + serializer.serializeSafe(internal, Objects.requireNonNull(val)); + return val; + } else { + try { + return deserializer.deserialize(internal); + } catch (Exception e) { + throw new IllegalStateException(e); + } + } + } + + @Override + public > C syncCollection(C initial, IByteBufSerializer serializer, + IByteBufDeserializer deserializer) { + if (isServer()) { + internal.writeVarInt(initial.size()); + initial.forEach(t -> serializer.serializeSafe(internal, t)); + } else { + int size = internal.readVarInt(); + try { + for (int i = 0; i < size; i++) { + initial.add(deserializer.deserialize(internal)); + } + } catch (IOException e) { + throw new IllegalStateException(e); + } + } + return initial; + } + + @Override + public T[] syncArray(T[] initial, IByteBufSerializer serializer, IByteBufDeserializer deserializer) { + if (isServer()) { + internal.writeVarInt(initial.length); + for (T t : initial) { + serializer.serializeSafe(internal, t); + } + } else { + initial = Arrays.copyOf(initial, internal.readVarInt()); + Arrays.setAll(initial, i -> deserializer.deserializeSafe(internal)); + } + return initial; + } + + public void readBuffer(ByteBuf buf) { + clear(); + internal.clear(); + internal.writeBytes(buf); + } + + public void writeBuffer(ByteBuf buf) { + buf.writeBytes(internal); + } + + public boolean hasChanged() { + byte[] old = internal.array().clone(); + this.internal.clear(); + clear(); + onRebuild(); + runAction(); + return !Arrays.equals(old, internal.array()); + } + } + + public class InternalSyncHandler extends SyncHandler { + + private InternalSyncHandler() {} + + @Override + public void detectAndSendChanges(boolean init) { + if (init || hasChanged()) { + if (init) { + onRebuild(); + runAction(); + } + syncToClient(0, buf -> getSyncer().writeBuffer(buf)); + } + } + + @Override + public void readOnClient(int id, PacketBuffer buf) { + if (id == 0) { + getSyncer().readBuffer(buf); + onRebuild(); + runAction(); + } + } + + @Override + public void readOnServer(int id, PacketBuffer buf) {} + } +} diff --git a/src/main/java/gregtech/api/metatileentity/multiblock/ui/MultiblockUIFactory.java b/src/main/java/gregtech/api/metatileentity/multiblock/ui/MultiblockUIFactory.java new file mode 100644 index 00000000000..495a80a8aa8 --- /dev/null +++ b/src/main/java/gregtech/api/metatileentity/multiblock/ui/MultiblockUIFactory.java @@ -0,0 +1,503 @@ +package gregtech.api.metatileentity.multiblock.ui; + +import gregtech.api.capability.IControllable; +import gregtech.api.capability.IDistinctBusController; +import gregtech.api.metatileentity.multiblock.MultiblockWithDisplayBase; +import gregtech.api.metatileentity.multiblock.ProgressBarMultiblock; +import gregtech.api.mui.GTGuiTextures; +import gregtech.api.mui.GTGuis; +import gregtech.api.util.GTLambdaUtils; +import gregtech.api.util.KeyUtil; +import gregtech.common.mui.widget.ScrollableTextWidget; + +import net.minecraft.util.text.TextFormatting; + +import com.cleanroommc.modularui.api.drawable.IDrawable; +import com.cleanroommc.modularui.api.drawable.IKey; +import com.cleanroommc.modularui.api.value.IBoolValue; +import com.cleanroommc.modularui.api.widget.IWidget; +import com.cleanroommc.modularui.drawable.DynamicDrawable; +import com.cleanroommc.modularui.drawable.Icon; +import com.cleanroommc.modularui.drawable.Rectangle; +import com.cleanroommc.modularui.drawable.UITexture; +import com.cleanroommc.modularui.factory.PosGuiData; +import com.cleanroommc.modularui.screen.ModularPanel; +import com.cleanroommc.modularui.utils.Alignment; +import com.cleanroommc.modularui.utils.Color; +import com.cleanroommc.modularui.value.BoolValue; +import com.cleanroommc.modularui.value.sync.BooleanSyncValue; +import com.cleanroommc.modularui.value.sync.IntSyncValue; +import com.cleanroommc.modularui.value.sync.PanelSyncManager; +import com.cleanroommc.modularui.widget.ParentWidget; +import com.cleanroommc.modularui.widget.Widget; +import com.cleanroommc.modularui.widgets.CycleButtonWidget; +import com.cleanroommc.modularui.widgets.ProgressWidget; +import com.cleanroommc.modularui.widgets.SlotGroupWidget; +import com.cleanroommc.modularui.widgets.ToggleButton; +import com.cleanroommc.modularui.widgets.layout.Flow; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import java.util.ArrayList; +import java.util.List; +import java.util.function.BiFunction; +import java.util.function.Consumer; +import java.util.function.UnaryOperator; + +public class MultiblockUIFactory { + + private static final Consumer NO_OP = b -> {}; + private static final IBoolValue ALWAYS_ON = new BoolValue.Dynamic(() -> true, b -> {}); + + private final MultiblockWithDisplayBase mte; + protected Consumer displayText, warningText, errorText; + protected BiFunction flexButton = (guiData, syncManager) -> null; + private int width = 198, height = 202; + private int screenHeight = 109; + private ScreenFunction screenFunction; + private boolean disableDisplay = false; + private boolean disableIndicator = false; + private boolean disableButtons = false; + + public MultiblockUIFactory(@NotNull MultiblockWithDisplayBase mte) { + this.mte = mte; + configureDisplayText(builder -> builder.title(mte.getMetaFullName()).structureFormed(mte.isStructureFormed())); + } + + private Widget createIndicator(PanelSyncManager syncManager) { + if (warningText == NO_OP && errorText == NO_OP) { + return new Widget<>() + .debugName("indicator_none") + .size(18) + .pos(174 - 5, screenHeight - 18 - 3) + .overlay(GTGuiTextures.GREGTECH_LOGO_DARK); + } + + MultiblockUIBuilder error = builder(); + error.sync("error", syncManager); + error.setAction(this.errorText); + + MultiblockUIBuilder warning = builder(); + warning.sync("warning", syncManager); + warning.setAction(this.warningText); + warning.onRebuild(() -> warning.updateFormed(mte.isStructureFormed())); + + IDrawable indicator = new DynamicDrawable(() -> { + if (!error.isEmpty()) { + return GTGuiTextures.GREGTECH_LOGO_BLINKING_RED; + } else if (!warning.isEmpty()) { + return GTGuiTextures.GREGTECH_LOGO_BLINKING_YELLOW; + } else { + return GTGuiTextures.GREGTECH_LOGO_DARK; + } + }); + + return new Widget<>() + .debugName("indicator") + .size(18) + .pos(174 - 5, screenHeight - 18 - 3) + .overlay(indicator) + .tooltipAutoUpdate(true) + .tooltipBuilder(t -> { + if (!error.isEmpty()) { + error.build(t); + } else if (!warning.isEmpty()) { + warning.build(t); + } + }); + } + + /** + * Returns a list of text indicating any current warnings in this Multiblock.
+ * Recommended to only display warnings if the structure is already formed.
+ * This is called every tick on the client-side + */ + public MultiblockUIFactory configureWarningText(boolean merge, Consumer warningText) { + this.warningText = merge ? GTLambdaUtils.mergeConsumers(this.warningText, warningText) : warningText; + return this; + } + + /** + * Returns a list of text indicating any current warnings in this Multiblock.
+ * Recommended to only display warnings if the structure is already formed.
+ * This is called every tick on the client-side + */ + public MultiblockUIFactory configureWarningText(Consumer warningText) { + return configureWarningText(true, warningText); + } + + public MultiblockUIFactory disableWarningText() { + this.warningText = NO_OP; + return this; + } + + /** + * Returns a list of translation keys indicating any current errors in this Multiblock.
+ * Prioritized over any warnings provided by {@link #configureWarningText(Consumer)}.
+ * This is called every tick on the client-side + */ + public MultiblockUIFactory configureErrorText(boolean merge, Consumer errorText) { + this.errorText = merge ? GTLambdaUtils.mergeConsumers(this.errorText, errorText) : errorText; + return this; + } + + /** + * Returns a list of translation keys indicating any current errors in this Multiblock.
+ * Prioritized over any warnings provided by {@link #configureWarningText(Consumer)}.
+ * This is called every tick on the client-side + */ + public MultiblockUIFactory configureErrorText(Consumer errorText) { + return configureErrorText(true, errorText); + } + + public MultiblockUIFactory disableErrorText() { + this.errorText = NO_OP; + return this; + } + + /** + * Called per tick on client side
+ * Each element of list is displayed on new line
+ * To use translation, use {@link KeyUtil#lang(TextFormatting, String, Object...)} + * or {@link KeyUtil#lang(String, Object...)} + */ + public MultiblockUIFactory configureDisplayText(boolean merge, Consumer displayText) { + this.displayText = merge ? GTLambdaUtils.mergeConsumers(this.displayText, displayText) : displayText; + return this; + } + + /** + * Called per tick on client side
+ * Each element of list is displayed on new line
+ * To use translation, use {@link KeyUtil#lang(TextFormatting, String, Object...)} + * or {@link KeyUtil#lang(String, Object...)} + */ + public MultiblockUIFactory configureDisplayText(Consumer displayText) { + return configureDisplayText(true, displayText); + } + + public MultiblockUIFactory disableDisplayText() { + this.displayText = NO_OP; + return this; + } + + public MultiblockUIFactory disableDisplay() { + disableDisplayText(); + this.disableDisplay = true; + return disableIndicator(); + } + + public MultiblockUIFactory disableIndicator() { + disableWarningText(); + disableErrorText(); + this.disableIndicator = true; + return this; + } + + public MultiblockUIFactory disableButtons() { + this.disableButtons = true; + return this; + } + + /** + * Add a custom third button to the Multiblock UI. By default, this is a placeholder stating that there is no + * additional functionality for this Multiblock.
+ * Size will be 18x18.
+ * Return {@code null} in the function to indicate no flex button + */ + public MultiblockUIFactory createFlexButton(BiFunction flexButton) { + this.flexButton = flexButton; + return this; + } + + public MultiblockUIFactory setSize(int width, int height) { + this.width = width; + this.height = height; + return this; + } + + public MultiblockUIFactory setScreenHeight(int height) { + int diff = height - this.screenHeight; + this.height += diff; + this.screenHeight += diff; + return this; + } + + public MultiblockUIFactory addScreenChildren(ScreenFunction function) { + this.screenFunction = function; + return this; + } + + /** + * Constructs the multiblock ui panel
+ * It is not recommended to override this method + */ + public @NotNull ModularPanel buildUI(PosGuiData guiData, PanelSyncManager panelSyncManager) { + var panel = GTGuis.createPanel(mte, width, height) + .debugName("root_panel") + .childIf(!disableDisplay, () -> createScreen(panelSyncManager)); + + // TODO createExtras() hook for overrides? + if (mte instanceof ProgressBarMultiblock progressBarMultiblock && progressBarMultiblock.hasBars()) { + panel.height(height + (Bars.HEIGHT * calculateRows(progressBarMultiblock.getProgressBarCount())) - 2); + panel.child(createBars(progressBarMultiblock, panelSyncManager)); + } + + if (disableDisplay && screenFunction != null) { + this.screenFunction.addWidgets(panel, panelSyncManager); + } + + var playerInv = SlotGroupWidget.playerInventory(0); + if (disableButtons) { + playerInv.alignX(0.5f); + } else { + playerInv.left(4); + } + + return panel.child(Flow.row() + .debugName("bottom_row") + .bottom(7) + .coverChildrenHeight() + .margin(4, 0) + .crossAxisAlignment(Alignment.CrossAxis.CENTER) + .child(playerInv) + .childIf(!disableButtons, () -> createButtons(panel, panelSyncManager, guiData))); + } + + private static int calculateRows(int count) { + if (count <= 3) { + return 1; + } + + if (count <= 8) { + return 2; + } + + throw new UnsupportedOperationException("Cannot compute progress bar rows for count " + count); + } + + private static int calculateCols(int count, int row) { + return switch (count) { + case 0, 1, 2, 3 -> count; + case 4 -> 2; + case 5 -> row == 0 ? 3 : 2; + case 6 -> 3; + case 7 -> row == 0 ? 4 : 3; + case 8 -> 4; + default -> throw new UnsupportedOperationException("Cannot compute progress bar cols for count " + count); + }; + } + + /** + * @param progressMulti the multiblock with progress bars + * @param panelSyncManager the sync manager for synchronizing widgets + */ + @Nullable + protected Flow createBars(@NotNull ProgressBarMultiblock progressMulti, + @NotNull PanelSyncManager panelSyncManager) { + final int count = progressMulti.getProgressBarCount(); + final int calculatedRows = calculateRows(count); + + Flow column = Flow.column() + .debugName("bar_col") + .margin(4, 0) + .top(5 + screenHeight) + .widthRel(1f) + .height(Bars.HEIGHT * calculatedRows); + + List> barBuilders = new ArrayList<>(progressMulti.getProgressBarCount()); + progressMulti.registerBars(barBuilders, panelSyncManager); + + for (int r = 0; r < calculatedRows; r++) { + + final int calculatedCols = calculateCols(count, r); + + Flow row = Flow.row() + .debugName("bar_row:" + r) + .widthRel(1f) + .mainAxisAlignment(Alignment.MainAxis.SPACE_BETWEEN) + .height(Bars.HEIGHT); + + // the numbers for the given row of bars + int from = r * (count - calculatedCols); + int to = from + calculatedCols; + + // calculate bar width + int barCount = Math.max(1, to - from); + int barWidth = (Bars.FULL_WIDTH / barCount) - (barCount - 1); + + for (int i = from; i < to; i++) { + ProgressWidget widget; + if (i < barBuilders.size()) { + widget = barBuilders.get(i) + .apply(new TemplateBarBuilder()) + .build(); + } else { + widget = new ProgressWidget() + .addTooltipLine("Error! no bar for index: " + i) + .background(new Rectangle().setColor(Color.RED.main)); + } + + row.child(widget.size(barWidth, Bars.HEIGHT) + .debugName(mte.getClass().getSimpleName() + ":bar:" + i) + .direction(ProgressWidget.Direction.RIGHT)); + } + + column.child(row); + } + return column; + } + + protected Widget createScreen(PanelSyncManager syncManager) { + var parent = new ParentWidget<>(); + + if (displayText != NO_OP) { + MultiblockUIBuilder display = builder(); + display.setAction(this.displayText); + display.sync("display", syncManager); + + parent.child(new ScrollableTextWidget() + .debugName("display_text") + .sizeRel(1f) + .alignment(Alignment.TopLeft) + .margin(4, 4) + .autoUpdate(true) + .textBuilder(display::build)); + } + + if (this.screenFunction != null) { + this.screenFunction.addWidgets(parent, syncManager); + } + + return parent.childIf(!disableIndicator, () -> createIndicator(syncManager)) + .debugName("display_root") + .background(getDisplayBackground()) + .size(190, screenHeight) + .pos(4, 4); + } + + private UITexture getDisplayBackground() { + return mte.getUITheme().getDisplayBackground(); + } + + @NotNull + protected Flow createButtons(@NotNull ModularPanel mainPanel, @NotNull PanelSyncManager panelSyncManager, + PosGuiData guiData) { + IWidget flexButton = this.flexButton.apply(guiData, panelSyncManager); + if (flexButton == null) { + flexButton = new ToggleButton() + .debugName("flex_none") + .value(ALWAYS_ON) + .overlay(GTGuiTextures.OVERLAY_NO_FLEX) + .size(18) + .addTooltipLine(IKey.lang("gregtech.multiblock.universal.no_flex_button")); + } + var powerButton = createPowerButton(mainPanel, panelSyncManager); + + return Flow.column() + .debugName("button_col") + .right(4) + .coverChildren() + .child(createDistinctButton(mainPanel, panelSyncManager)) + .child(createVoidingButton(mainPanel, panelSyncManager)) + .child(flexButton) + .childIf(powerButton != null, powerButton); + } + + protected IWidget createDistinctButton(@NotNull ModularPanel mainPanel, + @NotNull PanelSyncManager panelSyncManager) { + if (!(mte instanceof IDistinctBusController distinct) || !distinct.canBeDistinct()) { + return new ToggleButton() + .debugName("distinct_none") + .value(ALWAYS_ON) + .size(18) + .overlay(GTGuiTextures.OVERLAY_DISTINCT_BUSES[0]) + .addTooltipLine(IKey.lang("gregtech.multiblock.universal.distinct_not_supported")); + } + + return new ToggleButton() + .debugName("distinct_button") + .size(18) + .value(new BooleanSyncValue(distinct::isDistinct, distinct::setDistinct)) + .disableHoverBackground() + .overlay(true, GTGuiTextures.OVERLAY_DISTINCT_BUSES[1]) + .overlay(false, GTGuiTextures.OVERLAY_DISTINCT_BUSES[0]) + .addTooltip(true, IKey.lang("gregtech.multiblock.universal.distinct_enabled")) + .addTooltip(false, IKey.lang("gregtech.multiblock.universal.distinct_disabled")); + } + + protected IWidget createVoidingButton(@NotNull ModularPanel mainPanel, @NotNull PanelSyncManager panelSyncManager) { + if (!mte.shouldShowVoidingModeButton()) { + return new ToggleButton() + .debugName("voiding_none") + .value(ALWAYS_ON) + .size(18) + .overlay(GTGuiTextures.OVERLAY_VOID_NONE) + .addTooltipLine(IKey.lang("gregtech.gui.multiblock_voiding_not_supported")); + } + + IntSyncValue voidingValue = new IntSyncValue(mte::getVoidingMode, mte::setVoidingMode); + + return new CycleButtonWidget() + .debugName("voiding_button") + .size(18) + .value(voidingValue) + .length(4) + .stateOverlay(0, GTGuiTextures.MULTIBLOCK_VOID[0]) + .stateOverlay(1, GTGuiTextures.MULTIBLOCK_VOID[1]) + .stateOverlay(2, GTGuiTextures.MULTIBLOCK_VOID[2]) + .stateOverlay(3, GTGuiTextures.MULTIBLOCK_VOID[3]) + .tooltipBuilder(t -> t.addLine(IKey.lang(mte.getVoidingModeTooltip(voidingValue.getIntValue())))); + } + + @Nullable + protected Widget createPowerButton(@NotNull ModularPanel mainPanel, @NotNull PanelSyncManager panelSyncManager) { + if (mte instanceof IControllable controllable) { + Icon detail = GTGuiTextures.BUTTON_POWER_DETAIL.asIcon().size(18, 6).marginTop(24); + BooleanSyncValue controllableSync = new BooleanSyncValue(controllable::isWorkingEnabled, + controllable::setWorkingEnabled); + + return new ToggleButton() + .debugName("power_button") + .size(18) + .disableHoverBackground() + .overlay(true, detail, GTGuiTextures.BUTTON_POWER[1]) + .overlay(false, detail, GTGuiTextures.BUTTON_POWER[0]) + .value(controllableSync) + .marginTop(4); + } + + return null; + } + + public static final class Screen { + + public static int WIDTH = 190; + + private Screen() {} + } + + public static final class Bars { + + public static int FULL_WIDTH = Screen.WIDTH; + public static int HEIGHT = 7; + + private Bars() {} + } + + @FunctionalInterface + public interface ScreenFunction { + + void addWidgets(ParentWidget parent, PanelSyncManager syncManager); + } + + public static MultiblockUIBuilder builder() { + return new MultiblockUIBuilder(); + } + + public static MultiblockUIBuilder builder(String key, PanelSyncManager syncManager) { + var b = builder(); + b.sync(key, syncManager); + return b; + } +} diff --git a/src/main/java/gregtech/api/metatileentity/multiblock/ui/Operation.java b/src/main/java/gregtech/api/metatileentity/multiblock/ui/Operation.java new file mode 100644 index 00000000000..48736198a35 --- /dev/null +++ b/src/main/java/gregtech/api/metatileentity/multiblock/ui/Operation.java @@ -0,0 +1,44 @@ +package gregtech.api.metatileentity.multiblock.ui; + +import com.cleanroommc.modularui.api.drawable.IDrawable; +import com.cleanroommc.modularui.api.drawable.IRichTextBuilder; +import org.jetbrains.annotations.NotNull; + +import java.util.function.Consumer; + +@FunctionalInterface +public interface Operation extends Consumer> { + + Operation NO_OP = richText -> {}; + + static Operation addLine(IDrawable drawable) { + return richText -> richText.addLine(drawable); + } + + static Operation add(IDrawable drawable) { + return richText -> richText.add(drawable); + } + + static Operation addLineSpace(IDrawable drawable) { + return addLine(drawable).spaceLine(2); + } + + @Override + void accept(IRichTextBuilder richText); + + @NotNull + default Operation spaceLine(int space) { + return richText -> { + this.accept(richText); + richText.spaceLine(space); + }; + } + + @NotNull + default Operation newLine() { + return richText -> { + this.accept(richText); + richText.newLine(); + }; + } +} diff --git a/src/main/java/gregtech/api/metatileentity/multiblock/ui/TemplateBarBuilder.java b/src/main/java/gregtech/api/metatileentity/multiblock/ui/TemplateBarBuilder.java new file mode 100644 index 00000000000..e3ad7d60a67 --- /dev/null +++ b/src/main/java/gregtech/api/metatileentity/multiblock/ui/TemplateBarBuilder.java @@ -0,0 +1,41 @@ +package gregtech.api.metatileentity.multiblock.ui; + +import com.cleanroommc.modularui.api.value.IDoubleValue; +import com.cleanroommc.modularui.drawable.UITexture; +import com.cleanroommc.modularui.screen.RichTooltip; +import com.cleanroommc.modularui.value.DoubleValue; +import com.cleanroommc.modularui.widgets.ProgressWidget; + +import java.util.function.Consumer; +import java.util.function.DoubleSupplier; + +public final class TemplateBarBuilder { + + ProgressWidget widget = new ProgressWidget(); + + TemplateBarBuilder() {} + + public TemplateBarBuilder progress(DoubleSupplier supplier) { + return value(new DoubleValue.Dynamic(supplier, d -> {})); + } + + public TemplateBarBuilder value(IDoubleValue value) { + this.widget.value(value); + return this; + } + + public TemplateBarBuilder texture(UITexture texture) { + this.widget.texture(texture, -1); + return this; + } + + public TemplateBarBuilder tooltipBuilder(Consumer consumer) { + this.widget.tooltipAutoUpdate(true); + this.widget.tooltipBuilder(consumer); + return this; + } + + ProgressWidget build() { + return this.widget; + } +} diff --git a/src/main/java/gregtech/api/metatileentity/multiblock/ui/UISyncer.java b/src/main/java/gregtech/api/metatileentity/multiblock/ui/UISyncer.java new file mode 100644 index 00000000000..dd90d7cd367 --- /dev/null +++ b/src/main/java/gregtech/api/metatileentity/multiblock/ui/UISyncer.java @@ -0,0 +1,214 @@ +package gregtech.api.metatileentity.multiblock.ui; + +import gregtech.api.mui.GTByteBufAdapters; +import gregtech.api.util.function.ByteSupplier; +import gregtech.api.util.function.FloatSupplier; + +import net.minecraft.item.ItemStack; +import net.minecraftforge.fluids.Fluid; +import net.minecraftforge.fluids.FluidStack; + +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 org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import java.math.BigInteger; +import java.util.Collection; +import java.util.function.BooleanSupplier; +import java.util.function.DoubleSupplier; +import java.util.function.IntSupplier; +import java.util.function.LongSupplier; +import java.util.function.Supplier; + +public interface UISyncer { + + /** + * Calls the supplier server side only so there's no potential NPEs for the client + * + * @param initial supplier to be called on the server + * @return synced value + */ + boolean syncBoolean(@NotNull BooleanSupplier initial); + + /** + * Calls the supplier server side only so there's no potential NPEs for the client + * + * @param initial supplier to be called on the server + * @return synced value + */ + int syncInt(@NotNull IntSupplier initial); + + /** + * Calls the supplier server side only so there's no potential NPEs for the client + * + * @param initial supplier to be called on the server + * @return synced value + */ + long syncLong(@NotNull LongSupplier initial); + + /** + * Calls the supplier server side only so there's no potential NPEs for the client + * + * @param initial supplier to be called on the server + * @return synced value + */ + byte syncByte(@NotNull ByteSupplier initial); + + /** + * Calls the supplier server side only so there's no potential NPEs for the client + * + * @param initial supplier to be called on the server + * @return synced value + */ + double syncDouble(@NotNull DoubleSupplier initial); + + /** + * Calls the supplier server side only so there's no potential NPEs for the client + * + * @param initial supplier to be called on the server + * @return synced value + */ + float syncFloat(@NotNull FloatSupplier initial); + + default boolean syncBoolean(boolean initial) { + return syncBoolean(() -> initial); + } + + default int syncInt(int initial) { + return syncInt(() -> initial); + } + + default long syncLong(long initial) { + return syncLong(() -> initial); + } + + default byte syncByte(byte initial) { + return syncByte(() -> initial); + } + + default double syncDouble(double initial) { + return syncDouble(() -> initial); + } + + default float syncFloat(float initial) { + return syncFloat(() -> initial); + } + + default @NotNull String syncString(@NotNull String initial) { + return syncObject(initial, ByteBufAdapters.STRING); + } + + default BigInteger syncBigInt(BigInteger initial) { + return syncObject(initial, GTByteBufAdapters.BIG_INT); + } + + default T syncObject(T initial, IByteBufSerializer serializer, IByteBufDeserializer deserializer) { + return syncObject((Supplier) () -> initial, serializer, deserializer); + } + + default T syncObject(T initial, IByteBufAdapter adapter) { + return syncObject(initial, adapter, adapter); + } + + T syncObject(@NotNull Supplier<@NotNull T> initial, IByteBufSerializer serializer, + IByteBufDeserializer deserializer); + + default T syncObject(@NotNull Supplier<@NotNull T> initial, IByteBufAdapter adapter) { + return syncObject(initial, adapter, adapter); + } + + /** + * Syncs the elements of the collection to the internal buffer.
+ * On the server, elements are serialized in the order of iteration.
+ * On the client, elements are deserialized in the same order and re-added to the collection. + * The list will be modified on the client + * + * @param initial collection whose elements should be synced + * @param serializer serializer to write an element to the buffer + * @param deserializer deserializer to read the elements to add them back to the collection + * @param collection element's type + * @param the collection type itself + * @return the synced collection + */ + > C syncCollection(C initial, + IByteBufSerializer serializer, + IByteBufDeserializer deserializer); + + /** + * Syncs the elements of the collection to the internal buffer.
+ * On the server, elements are serialized in the order of iteration.
+ * On the client, elements are deserialized in the same order and re-added to the collection. + * The list will be modified on the client + * + * @param initial collection whose elements should be synced + * @param adapter adapter for serialization/deserialization + * @param collection element's type + * @param the collection type itself + * @return the synced collection + */ + default > C syncCollection(C initial, IByteBufAdapter adapter) { + return syncCollection(initial, adapter, adapter); + } + + /** + * Syncs the elements of the array to the internal buffer.
+ * On the server, elements are serialized in the order of iteration.
+ * On the client, elements are deserialized in the same order.
+ * The array will not be modified, instead copied + * + * @param initial array whose elements should be synced + * @param serializer serializer to write an element to the buffer + * @param deserializer deserializer to read the elements to add them back to the collection + * @param element type + * @return on the server, the initial array, otherwise the synced array copy + */ + T[] syncArray(T[] initial, IByteBufSerializer serializer, IByteBufDeserializer deserializer); + + /** + * Syncs the elements of the array to the internal buffer.
+ * On the server, elements are serialized in the order of iteration.
+ * On the client, elements are deserialized in the same order.
+ * The array will not be modified, instead copied + * + * @param initial array whose elements should be synced + * @param adapter adapter for serialization/deserialization + * @param element type + * @return on the server, the initial array, otherwise the synced array copy + */ + default T[] syncArray(T[] initial, IByteBufAdapter adapter) { + return syncArray(initial, adapter, adapter); + } + + @NotNull + default ItemStack syncItemStack(@NotNull Supplier<@NotNull ItemStack> initial) { + return syncObject(initial, ByteBufAdapters.ITEM_STACK); + } + + @NotNull + default ItemStack syncItemStack(@NotNull ItemStack initial) { + return syncItemStack(() -> initial); + } + + @Nullable + default FluidStack syncFluidStack(@NotNull Supplier<@Nullable FluidStack> initial) { + return syncObject(initial, ByteBufAdapters.FLUID_STACK); + } + + @Nullable + default FluidStack syncFluidStack(@Nullable FluidStack initial) { + return syncFluidStack(() -> initial); + } + + @Nullable + default Fluid syncFluid(@NotNull Supplier<@Nullable Fluid> initial) { + return syncObject(initial, GTByteBufAdapters.FLUID); + } + + @Nullable + default Fluid syncFluid(@Nullable Fluid initial) { + return syncFluid(() -> initial); + } +} 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..8ace8f6a5f3 --- /dev/null +++ b/src/main/java/gregtech/api/mui/GTByteBufAdapters.java @@ -0,0 +1,58 @@ +package gregtech.api.mui; + +import gregtech.api.recipes.chance.output.impl.ChancedFluidOutput; +import gregtech.api.recipes.chance.output.impl.ChancedItemOutput; +import gregtech.api.util.NetworkUtil; + +import net.minecraft.network.PacketBuffer; +import net.minecraftforge.fluids.Fluid; + +import com.cleanroommc.modularui.utils.serialization.*; +import org.jetbrains.annotations.NotNull; + +import java.io.IOException; +import java.math.BigInteger; +import java.util.Objects; + +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 final IByteBufAdapter FLUID = makeAdapter(NetworkUtil::readFluid, NetworkUtil::writeFluid); + + 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, + @NotNull IEquals equals) { + final IEquals tester = Objects.requireNonNull(equals); + 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/GTGuiTextures.java b/src/main/java/gregtech/api/mui/GTGuiTextures.java index f34c08a936a..d1a46722ba4 100644 --- a/src/main/java/gregtech/api/mui/GTGuiTextures.java +++ b/src/main/java/gregtech/api/mui/GTGuiTextures.java @@ -5,6 +5,8 @@ import com.cleanroommc.modularui.api.drawable.IDrawable; import com.cleanroommc.modularui.api.drawable.IKey; import com.cleanroommc.modularui.drawable.UITexture; +import com.cleanroommc.modularui.screen.viewport.GuiContext; +import com.cleanroommc.modularui.theme.WidgetTheme; import org.jetbrains.annotations.ApiStatus; import org.jetbrains.annotations.NotNull; @@ -21,20 +23,32 @@ public class GTGuiTextures { /** Keys used for GT assets registered for use in Themes */ public static class IDs { - public static final String STANDARD_BACKGROUND = "gregtech_standard_bg"; - public static final String COVER_BACKGROUND = "gregtech_cover_bg"; - public static final String BRONZE_BACKGROUND = "gregtech_bronze_bg"; - public static final String STEEL_BACKGROUND = "gregtech_steel_bg"; - public static final String PRIMITIVE_BACKGROUND = "gregtech_primitive_bg"; + public static final String STANDARD_BACKGROUND = id("standard_bg"); + public static final String COVER_BACKGROUND = id("cover_bg"); + public static final String BRONZE_BACKGROUND = id("bronze_bg"); + public static final String STEEL_BACKGROUND = id("steel_bg"); + public static final String PRIMITIVE_BACKGROUND = id("primitive_bg"); - public static final String STANDARD_SLOT = "gregtech_standard_slot"; - public static final String BRONZE_SLOT = "gregtech_bronze_slot"; - public static final String STEEL_SLOT = "gregtech_steel_slot"; - public static final String PRIMITIVE_SLOT = "gregtech_primitive_slot"; + public static final String STANDARD_SLOT = id("standard_slot"); + public static final String BRONZE_SLOT = id("bronze_slot"); + public static final String STEEL_SLOT = id("steel_slot"); + public static final String PRIMITIVE_SLOT = id("primitive_slot"); - public static final String STANDARD_FLUID_SLOT = "gregtech_standard_fluid_slot"; + public static final String STANDARD_FLUID_SLOT = id("standard_fluid_slot"); - public static final String STANDARD_BUTTON = "gregtech_standard_button"; + public static final String STANDARD_BUTTON = id("standard_button"); + public static final String BRONZE_BUTTON = id("bronze_button"); + public static final String STEEL_BUTTON = id("steel_button"); + public static final String BRONZE_BUTTON_SELECTED = id("bronze_button_sel"); + public static final String STEEL_BUTTON_SELECTED = id("steel_button_sel"); + + public static final String DISPLAY = id("display"); + public static final String DISPLAY_BRONZE = id("display_bronze"); + public static final String DISPLAY_STEEL = id("display_steel"); + + private static String id(String path) { + return "gregtech:" + path; + } } // ICONS @@ -43,7 +57,12 @@ public static class IDs { /** @apiNote You may want {@link GTGuiTextures#getLogo} instead. */ public static final UITexture GREGTECH_LOGO_XMAS = fullImage("textures/gui/icon/gregtech_logo_xmas.png"); public static final UITexture GREGTECH_LOGO_DARK = fullImage("textures/gui/icon/gregtech_logo_dark.png"); - // todo blinking GT logos + public static final IDrawable GREGTECH_LOGO_BLINKING_YELLOW = animated( + "textures/gui/icon/gregtech_logo_blinking_yellow.png", + 17, 34, false, 60); + public static final IDrawable GREGTECH_LOGO_BLINKING_RED = animated( + "textures/gui/icon/gregtech_logo_blinking_red.png", + 17, 34, false, 36); public static final UITexture INDICATOR_NO_ENERGY = fullImage("textures/gui/base/indicator_no_energy.png"); public static final UITexture INDICATOR_NO_STEAM_BRONZE = fullImage( @@ -98,6 +117,7 @@ public static class IDs { // DISPLAYS public static final UITexture DISPLAY = new UITexture.Builder() .location(GTValues.MODID, "textures/gui/base/display.png") + .name(IDs.DISPLAY) .imageSize(143, 75) .adaptable(2) .canApplyTheme() @@ -105,18 +125,31 @@ public static class IDs { public static final UITexture DISPLAY_BRONZE = new UITexture.Builder() .location(GTValues.MODID, "textures/gui/base/display_bronze.png") + .name(IDs.DISPLAY_BRONZE) .imageSize(143, 75) .adaptable(2) .build(); public static final UITexture DISPLAY_STEEL = new UITexture.Builder() .location(GTValues.MODID, "textures/gui/base/display_steel.png") + .name(IDs.DISPLAY_STEEL) .imageSize(143, 75) .adaptable(2) .build(); // todo primitive display? + // FUSION + public static final UITexture FUSION_REACTOR_MK1_TITLE = fullImage( + "textures/gui/widget/fusion_reactor_mk1_title.png"); + public static final UITexture FUSION_REACTOR_MK2_TITLE = fullImage( + "textures/gui/widget/fusion_reactor_mk2_title.png"); + public static final UITexture FUSION_REACTOR_MK3_TITLE = fullImage( + "textures/gui/widget/fusion_reactor_mk3_title.png"); + public static final UITexture FUSION_DIAGRAM = fullImage("textures/gui/widget/fusion_reactor_diagram.png"); + public static final UITexture FUSION_LEGEND = fullImage("textures/gui/widget/fusion_reactor_legend.png"); + public static final UITexture FUSION_PROGRESS = fullImage("textures/gui/progress_bar/fusion_diagram/stitched.png"); + // SLOTS public static final UITexture SLOT = new UITexture.Builder() .location(GTValues.MODID, "textures/gui/base/slot.png") @@ -156,6 +189,25 @@ public static class IDs { .canApplyTheme() .build(); + // HPCA Component icons + public static final UITexture BLANK_TRANSPARENT = fullImage("textures/gui/base/blank_transparent.png"); + public static final UITexture HPCA_COMPONENT_OUTLINE = fullImage("textures/gui/widget/hpca/component_outline.png"); + public static final UITexture HPCA_ICON_EMPTY_COMPONENT = fullImage("textures/gui/widget/hpca/empty_component.png"); + public static final UITexture HPCA_ICON_ADVANCED_COMPUTATION_COMPONENT = fullImage( + "textures/gui/widget/hpca/advanced_computation_component.png"); + public static final UITexture HPCA_ICON_BRIDGE_COMPONENT = fullImage( + "textures/gui/widget/hpca/bridge_component.png"); + public static final UITexture HPCA_ICON_COMPUTATION_COMPONENT = fullImage( + "textures/gui/widget/hpca/computation_component.png"); + public static final UITexture HPCA_ICON_ACTIVE_COOLER_COMPONENT = fullImage( + "textures/gui/widget/hpca/active_cooler_component.png"); + public static final UITexture HPCA_ICON_HEAT_SINK_COMPONENT = fullImage( + "textures/gui/widget/hpca/heat_sink_component.png"); + public static final UITexture HPCA_ICON_DAMAGED_ADVANCED_COMPUTATION_COMPONENT = fullImage( + "textures/gui/widget/hpca/damaged_advanced_computation_component.png"); + public static final UITexture HPCA_ICON_DAMAGED_COMPUTATION_COMPONENT = fullImage( + "textures/gui/widget/hpca/damaged_computation_component.png"); + public static final UITexture[] BUTTON_BLACKLIST = slice("textures/gui/widget/button_blacklist.png", 16, 32, 16, 16, true); public static final UITexture[] BUTTON_IGNORE_DAMAGE = slice("textures/gui/widget/button_filter_damage.png", @@ -215,6 +267,21 @@ public static class IDs { public static final UITexture MENU_OVERLAY = fullImage("textures/gui/overlay/menu_overlay.png"); public static final UITexture RECIPE_LOCK = fullImage("textures/gui/widget/lock.png"); + public static final UITexture PRIMITIVE_FURNACE_OVERLAY = fullImage( + "textures/gui/primitive/overlay_primitive_furnace.png"); + public static final UITexture PRIMITIVE_DUST_OVERLAY = fullImage( + "textures/gui/primitive/overlay_primitive_dust.png"); + public static final UITexture PRIMITIVE_INGOT_OVERLAY = fullImage( + "textures/gui/primitive/overlay_primitive_ingot.png"); + public static final UITexture PRIMITIVE_LARGE_FLUID_TANK = new UITexture.Builder() + .location(GTValues.MODID, "textures/gui/primitive/primitive_large_fluid_tank.png") + .fullImage() + .adaptable(2) + .build(); + public static final UITexture PRIMITIVE_LARGE_FLUID_TANK_OVERLAY = fullImage( + "textures/gui/primitive/primitive_large_fluid_tank_overlay.png"); + public static final UITexture PRIMITIVE_BLAST_FURNACE_PROGRESS_BAR = fullImage( + "textures/gui/primitive/progress_bar_primitive_blast_furnace.png"); // todo bronze/steel/primitive fluid slots? @@ -269,8 +336,7 @@ public static class IDs { "textures/gui/overlay/extractor_overlay_steel.png"); public static final UITexture FILTER_SLOT_OVERLAY = fullImage("textures/gui/overlay/filter_slot_overlay.png", true); public static final UITexture FILTER_SETTINGS_OVERLAY = fullImage( - "textures/gui/overlay/filter_settings_overlay.png", - true); + "textures/gui/overlay/filter_settings_overlay.png"); public static final UITexture FURNACE_OVERLAY_1 = fullImage("textures/gui/overlay/furnace_overlay_1.png", true); public static final UITexture FURNACE_OVERLAY_2 = fullImage("textures/gui/overlay/furnace_overlay_2.png", true); public static final UITexture FURNACE_OVERLAY_BRONZE = fullImage("textures/gui/overlay/furnace_overlay_bronze.png"); @@ -336,6 +402,38 @@ public static class IDs { .canApplyTheme() .build(); + public static final UITexture BUTTON_BRONZE = new UITexture.Builder() + .location(GTValues.MODID, "textures/gui/widget/buttons.png") + .imageSize(54, 36) + .uv(18, 0, 18, 18) + .adaptable(2) + .name(IDs.BRONZE_BUTTON) + .build(); + + public static final UITexture BUTTON_BRONZE_SELECTED = new UITexture.Builder() + .location(GTValues.MODID, "textures/gui/widget/buttons.png") + .imageSize(54, 36) + .uv(18, 18, 18, 18) + .adaptable(2) + .name(IDs.BRONZE_BUTTON_SELECTED) + .build(); + + public static final UITexture BUTTON_STEEL = new UITexture.Builder() + .location(GTValues.MODID, "textures/gui/widget/buttons.png") + .imageSize(54, 36) + .uv(36, 0, 18, 18) + .adaptable(2) + .name(IDs.STEEL_BUTTON) + .build(); + + public static final UITexture BUTTON_STEEL_SELECTED = new UITexture.Builder() + .location(GTValues.MODID, "textures/gui/widget/buttons.png") + .imageSize(54, 36) + .uv(36, 18, 18, 18) + .adaptable(2) + .name(IDs.STEEL_BUTTON_SELECTED) + .build(); + public static final UITexture MC_BUTTON = new UITexture.Builder() .location("modularui", "gui/widgets/mc_button.png") // todo .imageSize(16, 32) @@ -362,8 +460,41 @@ public static class IDs { public static final UITexture BUTTON_CROSS = fullImage("textures/gui/widget/button_cross.png"); public static final UITexture BUTTON_REDSTONE_ON = fullImage("textures/gui/widget/button_redstone_on.png"); public static final UITexture BUTTON_REDSTONE_OFF = fullImage("textures/gui/widget/button_redstone_off.png"); - public static final UITexture BUTTON_THROTTLE_PLUS = fullImage("textures/gui/widget/button_throttle_plus.png"); - public static final UITexture BUTTON_THROTTLE_MINUS = fullImage("textures/gui/widget/button_throttle_minus.png"); + + /** + * 0 = OFF
+ * 1 = ON
+ */ + public static final UITexture[] BUTTON_POWER = slice("textures/gui/widget/button_power.png", + 18, 36, 18, 18, false); + + public static final UITexture BUTTON_POWER_DETAIL = fullImage("textures/gui/widget/button_power_detail.png"); + + /** + * 0 = DISABLED
+ * 1 = ITEM VOID
+ * 2 = FLUID VOID
+ * 3 = VOID BOTH
+ **/ + public static final UITexture[] MULTIBLOCK_VOID = slice("textures/gui/widget/button_void_multiblock.png", + 18, 72, 18, 18, false); + + public static final UITexture OVERLAY_VOID_NONE = fullImage("textures/gui/widget/button_void_none.png"); + + /** + * 0 = DISABLED
+ * 1 = ENABLED
+ */ + public static final UITexture[] OVERLAY_DISTINCT_BUSES = slice("textures/gui/widget/button_distinct_buses.png", + 18, 36, 18, 18, false); + + public static final UITexture OVERLAY_NO_FLEX = fullImage("textures/gui/widget/button_no_flex.png", false); + public static final UITexture BUTTON_MULTI_MAP = fullImage("textures/gui/widget/button_multi_map.png", true); + public static final UITexture BUTTON_MINER_MODES = fullImage("textures/gui/widget/button_miner_modes.png", true); + public static final UITexture BUTTON_THROTTLE_MINUS = fullImage("textures/gui/widget/button_throttle_minus.png", + true); // TODO new texture + public static final UITexture BUTTON_THROTTLE_PLUS = fullImage("textures/gui/widget/button_throttle_plus.png", + true); // TODO remove this // PROGRESS BARS public static final UITexture PROGRESS_BAR_ARC_FURNACE = progressBar( @@ -454,6 +585,7 @@ public static class IDs { "textures/gui/progress_bar/progress_bar_wiremill.png", true); // more custom progress bars + // MULTIBLOCK BARS // todo these boiler empty bars can probably be replaced by using a resized steam slot texture public static final UITexture PROGRESS_BAR_BOILER_EMPTY_BRONZE = new UITexture.Builder() .location(GTValues.MODID, "textures/gui/progress_bar/progress_bar_boiler_empty_bronze.png") @@ -470,25 +602,25 @@ public static class IDs { public static final UITexture PROGRESS_BAR_BOILER_FUEL_STEEL = progressBar( "textures/gui/progress_bar/progress_bar_boiler_fuel_steel.png", 18, 36); public static final UITexture PROGRESS_BAR_BOILER_HEAT = progressBar( - "textures/gui/progress_bar/progress_bar_boiler_heat.png", true); + "textures/gui/progress_bar/progress_bar_boiler_heat.png"); public static final UITexture PROGRESS_BAR_ASSEMBLY_LINE = progressBar( - "textures/gui/progress_bar/progress_bar_assembly_line.png", 54, 144, true); + "textures/gui/progress_bar/progress_bar_assembly_line.png", 54, 144); public static final UITexture PROGRESS_BAR_ASSEMBLY_LINE_ARROW = progressBar( - "textures/gui/progress_bar/progress_bar_assembly_line_arrow.png", 10, 36, true); + "textures/gui/progress_bar/progress_bar_assembly_line_arrow.png", 10, 36); public static final UITexture PROGRESS_BAR_COKE_OVEN = progressBar( - "textures/gui/progress_bar/progress_bar_coke_oven.png", 36, 36, true); + "textures/gui/progress_bar/progress_bar_coke_oven.png", 36, 36); public static final UITexture PROGRESS_BAR_DISTILLATION_TOWER = progressBar( - "textures/gui/progress_bar/progress_bar_distillation_tower.png", 66, 116, true); + "textures/gui/progress_bar/progress_bar_distillation_tower.png", 66, 116); public static final UITexture PROGRESS_BAR_SOLAR_BRONZE = progressBar( "textures/gui/progress_bar/progress_bar_solar_bronze.png", 10, 20); public static final UITexture PROGRESS_BAR_SOLAR_STEEL = progressBar( "textures/gui/progress_bar/progress_bar_solar_steel.png", 10, 20); public static final UITexture PROGRESS_BAR_RESEARCH_STATION_1 = progressBar( - "textures/gui/progress_bar/progress_bar_research_station_1.png", 54, 10, true); + "textures/gui/progress_bar/progress_bar_research_station_1.png", 54, 10); public static final UITexture PROGRESS_BAR_RESEARCH_STATION_2 = progressBar( - "textures/gui/progress_bar/progress_bar_research_station_2.png", 10, 36, true); + "textures/gui/progress_bar/progress_bar_research_station_2.png", 10, 36); public static final UITexture PROGRESS_BAR_RESEARCH_STATION_BASE = fullImage( - "textures/gui/progress_bar/progress_bar_research_station_base.png", true); + "textures/gui/progress_bar/progress_bar_research_station_base.png"); public static final UITexture PROGRESS_BAR_FUSION_ENERGY = progressBar( "textures/gui/progress_bar/progress_bar_fusion_energy.png", 94, 14); public static final UITexture PROGRESS_BAR_FUSION_HEAT = progressBar( @@ -545,6 +677,15 @@ private static UITexture[] slice(String path, int imageWidth, int imageHeight, i return slices; } + private static UITexture[] slice(String path, int imageWidth, int imageHeight, boolean canApplyTheme) { + int sliceSize = Math.min(imageWidth, imageHeight); + return slice(path, imageWidth, imageHeight, sliceSize, sliceSize, canApplyTheme); + } + + private static IDrawable animated(String path, int imageWidth, int imageHeight, boolean canApplyTheme, int rate) { + return dynamic(slice(path, imageWidth, imageHeight, canApplyTheme), rate); + } + private static UITexture progressBar(String path) { return progressBar(path, 20, 40, false); } @@ -573,4 +714,19 @@ private static UITexture progressBar(String path, int width, int height, boolean } return GTValues.XMAS.get() ? GREGTECH_LOGO_XMAS : GREGTECH_LOGO; } + + public static IDrawable dynamic(UITexture[] textures, int rate) { + return new IDrawable() { + + int tick = 0; + int index = 0; + + @Override + public void draw(GuiContext context, int x, int y, int width, int height, WidgetTheme widgetTheme) { + int a = tick++ % rate; // this makes rate per frame ? + if (a == 0) index++; + textures[index % textures.length].draw(context, x, y, width, height, widgetTheme); + } + }; + } } diff --git a/src/main/java/gregtech/api/mui/GTGuiTheme.java b/src/main/java/gregtech/api/mui/GTGuiTheme.java index 4a6c2c2d4dd..aa7b579797f 100644 --- a/src/main/java/gregtech/api/mui/GTGuiTheme.java +++ b/src/main/java/gregtech/api/mui/GTGuiTheme.java @@ -8,9 +8,11 @@ import com.cleanroommc.modularui.api.ITheme; import com.cleanroommc.modularui.api.IThemeApi; +import com.cleanroommc.modularui.drawable.DrawableSerialization; import com.cleanroommc.modularui.drawable.UITexture; import com.cleanroommc.modularui.screen.RichTooltip; import com.cleanroommc.modularui.theme.ReloadThemeEvent; +import com.cleanroommc.modularui.utils.Color; import com.cleanroommc.modularui.utils.JsonBuilder; import org.jetbrains.annotations.Nullable; @@ -19,54 +21,96 @@ import java.util.function.Consumer; import java.util.function.Supplier; +import static gregtech.api.mui.GTGuiTextures.*; + public class GTGuiTheme { + public static class Colors { + + public static final int BRONZE = 0xFF7706; + public static final int STEEL = 0x57576A; + public static final int PRIMITIVE = 0x826B51; + } + + public static class Names { + + public static final String STANDARD = gregtech("standard"); + public static final String COVER = gregtech("cover"); + public static final String BRONZE = gregtech("bronze"); + public static final String STEEL = gregtech("steel"); + public static final String PRIMITIVE = gregtech("primitive"); + + private static String gregtech(String s) { + return "gregtech:" + s; + } + } + private static final List THEMES = new ArrayList<>(); - public static final GTGuiTheme STANDARD = templateBuilder("gregtech_standard") - .panel(GTGuiTextures.IDs.STANDARD_BACKGROUND) - .itemSlot(GTGuiTextures.IDs.STANDARD_SLOT) - .fluidSlot(GTGuiTextures.IDs.STANDARD_FLUID_SLOT) + public static final GTGuiTheme STANDARD = templateBuilder(Names.STANDARD) + .panel(IDs.STANDARD_BACKGROUND) + .itemSlot(IDs.STANDARD_SLOT) + .fluidSlot(IDs.STANDARD_FLUID_SLOT) .color(ConfigHolder.client.defaultUIColor) - .button(GTGuiTextures.IDs.STANDARD_BUTTON) - .simpleToggleButton(GTGuiTextures.IDs.STANDARD_BUTTON, - GTGuiTextures.IDs.STANDARD_SLOT, + .button(IDs.STANDARD_BUTTON) + .simpleToggleButton(IDs.STANDARD_BUTTON, + IDs.STANDARD_SLOT, ConfigHolder.client.defaultUIColor) .build(); - public static final GTGuiTheme COVER = templateBuilder("gregtech_cover") - .panel(GTGuiTextures.IDs.COVER_BACKGROUND) - .itemSlot(GTGuiTextures.IDs.STANDARD_SLOT) - .fluidSlot(GTGuiTextures.IDs.STANDARD_FLUID_SLOT) + public static final GTGuiTheme COVER = templateBuilder(Names.COVER) + .panel(IDs.COVER_BACKGROUND) + .itemSlot(IDs.STANDARD_SLOT) + .fluidSlot(IDs.STANDARD_FLUID_SLOT) .color(ConfigHolder.client.defaultUIColor) .textColor(CoverWithUI.UI_TEXT_COLOR) .build(); // TODO Multiblock theme for display texture, logo changes - public static final GTGuiTheme BRONZE = templateBuilder("gregtech_bronze") - .panel(GTGuiTextures.IDs.BRONZE_BACKGROUND) - .itemSlot(GTGuiTextures.IDs.BRONZE_SLOT) + public static final GTGuiTheme BRONZE = templateBuilder(Names.BRONZE) + .parent(Names.STANDARD) + .panel(IDs.BRONZE_BACKGROUND) + // .itemSlot(GTGuiTextures.IDs.BRONZE_SLOT) + // .fluidSlot(GTGuiTextures.IDs.BRONZE_SLOT) + .displayBackground(IDs.DISPLAY_BRONZE) + .button(IDs.BRONZE_BUTTON) + .color(Colors.BRONZE) + .simpleToggleButton(IDs.BRONZE_BUTTON, IDs.BRONZE_BUTTON_SELECTED, + ConfigHolder.client.defaultUIColor) .build(); - public static final GTGuiTheme STEEL = templateBuilder("gregtech_steel") - .panel(GTGuiTextures.IDs.STEEL_BACKGROUND) - .itemSlot(GTGuiTextures.IDs.STEEL_SLOT) + public static final GTGuiTheme STEEL = templateBuilder(Names.STEEL) + .parent(Names.STANDARD) + .panel(IDs.STEEL_BACKGROUND) + .textColor(Color.WHITE.darker(1)) + // .itemSlot(GTGuiTextures.IDs.STEEL_SLOT) + // .fluidSlot(GTGuiTextures.IDs.STEEL_SLOT) + .displayBackground(IDs.DISPLAY_STEEL) + .button(IDs.STEEL_BUTTON) + .simpleToggleButton(IDs.STEEL_BUTTON, IDs.STEEL_BUTTON_SELECTED, + ConfigHolder.client.defaultUIColor) + .color(Colors.STEEL) .build(); - public static final GTGuiTheme PRIMITIVE = templateBuilder("gregtech_primitive") - .panel(GTGuiTextures.IDs.PRIMITIVE_BACKGROUND) - .itemSlot(GTGuiTextures.IDs.PRIMITIVE_SLOT) + public static final GTGuiTheme PRIMITIVE = templateBuilder(Names.PRIMITIVE) + .parent(Names.STANDARD) + .panel(IDs.PRIMITIVE_BACKGROUND) + .textColor(Color.WHITE.darker(1)) + .color(Colors.PRIMITIVE) + // .itemSlot(GTGuiTextures.IDs.PRIMITIVE_SLOT) + // .fluidSlot(GTGuiTextures.IDs.PRIMITIVE_SLOT) .build(); - private final String themeId; + protected final String themeId; - private final List> elementBuilder; - private final JsonBuilder jsonBuilder; + protected final List> elementBuilder; + protected final JsonBuilder jsonBuilder; private Supplier logo; + private String displayBackground = IDs.DISPLAY; - private GTGuiTheme(String themeId) { + protected GTGuiTheme(String themeId) { this.themeId = themeId; this.jsonBuilder = new JsonBuilder(); this.elementBuilder = new ArrayList<>(); @@ -86,6 +130,10 @@ public ITheme getMuiTheme() { return logo.get(); } + public @Nullable UITexture getDisplayBackground() { + return DrawableSerialization.getTexture(this.displayBackground); + } + private void register() { buildJson(); IThemeApi.get().registerTheme(themeId, jsonBuilder); @@ -391,6 +439,14 @@ public Builder logo(Supplier logo) { return this; } + /** + * Sets the display background for this theme. + */ + public Builder displayBackground(String displayBackground) { + theme.displayBackground = displayBackground; + return this; + } + public GTGuiTheme build() { return theme; } diff --git a/src/main/java/gregtech/api/mui/IconAcessor.java b/src/main/java/gregtech/api/mui/IconAcessor.java new file mode 100644 index 00000000000..2bf4218904f --- /dev/null +++ b/src/main/java/gregtech/api/mui/IconAcessor.java @@ -0,0 +1,8 @@ +package gregtech.api.mui; + +import com.cleanroommc.modularui.api.drawable.IDrawable; + +public interface IconAcessor { + + IDrawable gregTech$getDrawable(); +} 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..2a562ae63b0 --- /dev/null +++ b/src/main/java/gregtech/api/mui/drawable/GTObjectDrawable.java @@ -0,0 +1,102 @@ +package gregtech.api.mui.drawable; + +import gregtech.api.mui.GTGuiTextures; +import gregtech.api.recipes.chance.boost.BoostableChanceEntry; +import gregtech.api.util.TextFormattingUtil; + +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.integration.jei.JeiIngredientProvider; +import com.cleanroommc.modularui.screen.viewport.GuiContext; +import com.cleanroommc.modularui.screen.viewport.ModularGuiContext; +import com.cleanroommc.modularui.theme.WidgetSlotTheme; +import com.cleanroommc.modularui.theme.WidgetTheme; +import com.cleanroommc.modularui.utils.Alignment; +import com.cleanroommc.modularui.utils.Color; +import com.cleanroommc.modularui.widget.Widget; +import org.jetbrains.annotations.Nullable; + +import java.util.function.Function; + +public class GTObjectDrawable implements IDrawable, JeiIngredientProvider { + + 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) { + if (!(context instanceof ModularGuiContext modularGuiContext)) return; + renderer.setAlignment(Alignment.BottomRight, width - 1, height - 1); + drawObject(object, modularGuiContext, x, y, width, height); + if (amount > 0) { + renderer.setPos(x + 1, y + 1); + String amount = TextFormattingUtil.formatLongToCompactString(this.amount, 3); + if (object instanceof FluidStack) amount += "L"; + renderer.draw(amount); + } + } + + private void drawObject(Object object, ModularGuiContext context, int x, int y, int width, int height) { + if (object instanceof ItemStack stack) { + WidgetSlotTheme theme = context.getTheme().getItemSlotTheme(); + IDrawable background = theme.getBackground(); + if (background == null) background = GTGuiTextures.SLOT; + background.draw(context, x, y, width, height, theme); + GuiDraw.drawItem(stack, x + 1, y + 1, width - 2, height - 2); + } else if (object instanceof FluidStack stack) { + WidgetSlotTheme theme = context.getTheme().getFluidSlotTheme(); + IDrawable slot = theme.getBackground(); + if (slot == null) slot = GTGuiTextures.FLUID_SLOT; + slot.draw(context, x, y, width, height, theme); + GuiDraw.drawFluidTexture(stack, x + 1, y + 1, width - 2, height - 2, 0); + } else if (object instanceof BoostableChanceEntryentry) { + drawObject(entry.getIngredient(), context, x, y, width, height); + String chance = "~" + this.boostFunction.apply(entry) / 100 + "%"; + if (amount > 0) 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); + } + + @Override + public @Nullable Object getIngredient() { + if (object instanceof BoostableChanceEntryentry) { + return entry.getIngredient(); + } + return object; + } +} diff --git a/src/main/java/gregtech/api/mui/sync/BigIntegerSyncValue.java b/src/main/java/gregtech/api/mui/sync/BigIntegerSyncValue.java new file mode 100644 index 00000000000..5a4a397f9b8 --- /dev/null +++ b/src/main/java/gregtech/api/mui/sync/BigIntegerSyncValue.java @@ -0,0 +1,92 @@ +package gregtech.api.mui.sync; + +import net.minecraft.network.PacketBuffer; + +import com.cleanroommc.modularui.api.value.sync.IStringSyncValue; +import com.cleanroommc.modularui.network.NetworkUtils; +import com.cleanroommc.modularui.value.sync.ValueSyncHandler; +import org.jetbrains.annotations.Contract; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import java.io.IOException; +import java.math.BigInteger; +import java.util.Objects; +import java.util.function.Consumer; +import java.util.function.Supplier; + +public class BigIntegerSyncValue extends ValueSyncHandler implements IStringSyncValue { + + private BigInteger cache = BigInteger.ZERO; + private final Supplier getter; + private final @Nullable Consumer setter; + + public BigIntegerSyncValue(@NotNull Supplier<@NotNull BigInteger> getter, + @Nullable Consumer<@NotNull BigInteger> setter) { + this.getter = getter; + this.setter = setter; + } + + @Contract("null, _, null, _ -> fail") + public BigIntegerSyncValue(@Nullable Supplier<@NotNull BigInteger> clientGetter, + @Nullable Consumer<@NotNull BigInteger> clientSetter, + @Nullable Supplier<@NotNull BigInteger> serverGetter, + @Nullable Consumer<@NotNull BigInteger> serverSetter) { + if (clientGetter == null && serverGetter == null) { + throw new NullPointerException("Client or server getter must not be null!"); + } + if (NetworkUtils.isClient()) { + this.getter = clientGetter != null ? clientGetter : serverGetter; + this.setter = clientSetter != null ? clientSetter : serverSetter; + } else { + this.getter = serverGetter != null ? serverGetter : clientGetter; + this.setter = serverSetter != null ? serverSetter : clientSetter; + } + this.cache = this.getter.get(); + } + + @Override + public void setStringValue(String value, boolean setSource, boolean sync) { + setValue(new BigInteger(value), setSource, sync); + } + + @Override + public String getStringValue() { + return cache.toString(); + } + + @Override + public void setValue(BigInteger value, boolean setSource, boolean sync) { + this.cache = value; + if (setSource && this.setter != null) { + this.setter.accept(value); + } + if (sync) { + sync(0, this::write); + } + } + + @Override + public boolean updateCacheFromSource(boolean isFirstSync) { + if (this.getter != null && (isFirstSync || !Objects.equals(this.getter.get(), this.cache))) { + setValue(this.getter.get(), false, false); + return true; + } + return false; + } + + @Override + public void write(@NotNull PacketBuffer buffer) throws IOException { + buffer.writeByteArray(getValue().toByteArray()); + } + + @Override + public void read(@NotNull PacketBuffer buffer) throws IOException { + setValue(new BigInteger(buffer.readByteArray()), true, false); + } + + @Override + public BigInteger getValue() { + return this.cache; + } +} diff --git a/src/main/java/gregtech/api/mui/sync/FixedIntArraySyncValue.java b/src/main/java/gregtech/api/mui/sync/FixedIntArraySyncValue.java new file mode 100644 index 00000000000..c3f29ee80b5 --- /dev/null +++ b/src/main/java/gregtech/api/mui/sync/FixedIntArraySyncValue.java @@ -0,0 +1,95 @@ +package gregtech.api.mui.sync; + +import net.minecraft.network.PacketBuffer; + +import com.cleanroommc.modularui.network.NetworkUtils; +import com.cleanroommc.modularui.value.sync.ValueSyncHandler; +import org.jetbrains.annotations.Contract; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import java.io.IOException; +import java.util.Arrays; +import java.util.Objects; +import java.util.function.Consumer; +import java.util.function.Supplier; + +/** + * Sync Value for an array with fixed length. + *

+ * Does not create new arrays. + */ +public class FixedIntArraySyncValue extends ValueSyncHandler { + + private final int[] cache; + private final Supplier getter; + private final @Nullable Consumer setter; + + public FixedIntArraySyncValue(@NotNull Supplier getter, @Nullable Consumer setter) { + this.getter = Objects.requireNonNull(getter); + this.setter = setter; + this.cache = getter.get(); + } + + @Contract("null, _, null, _ -> fail") + public FixedIntArraySyncValue(@Nullable Supplier clientGetter, @Nullable Consumer clientSetter, + @Nullable Supplier serverGetter, @Nullable Consumer serverSetter) { + if (clientGetter == null && serverGetter == null) { + throw new NullPointerException("Client or server getter must not be null!"); + } + if (NetworkUtils.isClient()) { + this.getter = clientGetter != null ? clientGetter : serverGetter; + this.setter = clientSetter != null ? clientSetter : serverSetter; + } else { + this.getter = serverGetter != null ? serverGetter : clientGetter; + this.setter = serverSetter != null ? serverSetter : clientSetter; + } + this.cache = this.getter.get(); + } + + @Override + public void setValue(int @NotNull [] value, boolean setSource, boolean sync) { + if (value.length != cache.length) { + throw new IllegalArgumentException("Incompatible array lengths"); + } + System.arraycopy(value, 0, cache, 0, value.length); + if (setSource && this.setter != null) { + this.setter.accept(value); + } + if (sync) { + sync(0, this::write); + } + } + + @Override + public boolean updateCacheFromSource(boolean isFirstSync) { + if (isFirstSync || !Arrays.equals(this.getter.get(), this.cache)) { + setValue(this.getter.get(), false, false); + return true; + } + return false; + } + + @Override + public void write(@NotNull PacketBuffer buffer) throws IOException { + for (int i : cache) { + buffer.writeVarInt(i); + } + } + + @Override + public void read(@NotNull PacketBuffer buffer) throws IOException { + for (int i = 0; i < cache.length; i++) { + cache[i] = buffer.readVarInt(); + } + } + + @Override + public int[] getValue() { + return this.cache; + } + + public int getValue(int index) { + return this.cache[index]; + } +} diff --git a/src/main/java/gregtech/api/mui/sync/GTFluidSyncHandler.java b/src/main/java/gregtech/api/mui/sync/GTFluidSyncHandler.java index 4571c26e93d..54808e28719 100644 --- a/src/main/java/gregtech/api/mui/sync/GTFluidSyncHandler.java +++ b/src/main/java/gregtech/api/mui/sync/GTFluidSyncHandler.java @@ -1,6 +1,8 @@ package gregtech.api.mui.sync; +import gregtech.api.util.FluidTooltipUtil; import gregtech.api.util.GTUtility; +import gregtech.api.util.KeyUtil; import gregtech.common.covers.filter.readers.SimpleFluidFilterReader; import net.minecraft.entity.item.EntityItem; @@ -15,7 +17,9 @@ import net.minecraftforge.fluids.IFluidTank; import net.minecraftforge.fluids.capability.IFluidHandlerItem; +import com.cleanroommc.modularui.api.drawable.IKey; import com.cleanroommc.modularui.network.NetworkUtils; +import com.cleanroommc.modularui.screen.RichTooltip; import com.cleanroommc.modularui.utils.BooleanConsumer; import com.cleanroommc.modularui.utils.MouseData; import com.cleanroommc.modularui.value.sync.SyncHandler; @@ -43,7 +47,9 @@ public class GTFluidSyncHandler extends SyncHandler { private boolean canDrainSlot = true; private boolean canFillSlot = true; private boolean phantom; - private BooleanSupplier showAmount = () -> true; + private BooleanSupplier showAmountInTooltip = () -> true; + private BooleanSupplier showAmountOnSlot = () -> true; + private BooleanSupplier drawAlwaysFull = () -> true; public GTFluidSyncHandler(IFluidTank tank) { this.tank = tank; @@ -95,7 +101,7 @@ public void setFluid(FluidStack fluid) { fluidTank.setFluid(fluid); } else { tank.drain(Integer.MAX_VALUE, true); - tank.fill(fluid, true); + if (fluid != null) tank.fill(fluid, true); } if (!isPhantom() || fluid == null) return; if (this.phantomFluid == null || this.phantomFluid.getFluid() != fluid.getFluid()) { @@ -142,20 +148,50 @@ public boolean isPhantom() { return phantom; } - public GTFluidSyncHandler showAmount(boolean showAmount) { - this.showAmount = () -> showAmount; + public GTFluidSyncHandler showAmountInTooltip(boolean showAmount) { + this.showAmountInTooltip = () -> showAmount; return this; } - public GTFluidSyncHandler showAmount(BooleanSupplier showAmount) { - this.showAmount = showAmount; + public GTFluidSyncHandler showAmountInTooltip(BooleanSupplier showAmount) { + this.showAmountInTooltip = showAmount; return this; } - public boolean showAmount() { + public boolean showAmountInTooltip() { if (!isPhantom() && phantomFluid != null) return false; - return this.showAmount.getAsBoolean(); + return this.showAmountInTooltip.getAsBoolean(); + } + + public GTFluidSyncHandler showAmountOnSlot(boolean showAmount) { + this.showAmountOnSlot = () -> showAmount; + return this; + } + + public GTFluidSyncHandler showAmountOnSlot(BooleanSupplier showAmount) { + this.showAmountOnSlot = showAmount; + return this; + } + + public boolean showAmountOnSlot() { + if (!isPhantom() && phantomFluid != null) + return false; + return this.showAmountOnSlot.getAsBoolean(); + } + + public GTFluidSyncHandler drawAlwaysFull(boolean drawAsFull) { + this.drawAlwaysFull = () -> drawAsFull; + return this; + } + + public GTFluidSyncHandler drawAlwaysFull(BooleanSupplier drawAsFull) { + this.drawAlwaysFull = drawAsFull; + return this; + } + + public boolean drawAlwaysFull() { + return this.drawAlwaysFull.getAsBoolean(); } public @NotNull String getFormattedFluidAmount() { @@ -163,6 +199,11 @@ public boolean showAmount() { return String.format("%,d", tankFluid == null ? 0 : tankFluid.amount); } + public int getFluidAmount() { + FluidStack tankFluid = tank.getFluid(); + return tankFluid == null ? 0 : tankFluid.amount; + } + public @Nullable String getFluidLocalizedName() { var tankFluid = this.tank.getFluid(); if (tankFluid == null && canLockFluid()) @@ -171,6 +212,39 @@ public boolean showAmount() { return tankFluid == null ? null : tankFluid.getLocalizedName(); } + public @NotNull IKey getFluidNameKey() { + FluidStack tankFluid = tank.getFluid(); + if (tankFluid == null && canLockFluid()) { + tankFluid = lockedFluid.get(); + } + return tankFluid == null ? IKey.EMPTY : KeyUtil.fluid(tankFluid); + } + + public void handleTooltip(@NotNull RichTooltip tooltip) { + tooltip.addLine(getFluidNameKey()); + + if (showAmountInTooltip()) { + tooltip.addLine(IKey.lang("gregtech.fluid.amount", getFluidAmount(), getCapacity())); + } + + if (isPhantom() && showAmountInTooltip()) { + tooltip.addLine(IKey.lang("modularui.fluid.phantom.control")); + } + + FluidStack tankFluid = getFluid(); + if (tankFluid == null) { + tankFluid = getLockedFluid(); + } + + if (tankFluid != null) { + FluidTooltipUtil.handleFluidTooltip(tooltip, tankFluid); + + if (showAmountInTooltip()) { + FluidTooltipUtil.addIngotMolFluidTooltip(tooltip, tankFluid); + } + } + } + @Override public void readOnClient(int id, PacketBuffer buf) { switch (id) { @@ -231,9 +305,9 @@ public void tryClickPhantom(MouseData data) { } } else { FluidStack cellFluid = fluidHandlerItem.drain(Integer.MAX_VALUE, false); - if ((this.showAmount.getAsBoolean() || currentFluid == null) && cellFluid != null) { + if ((this.showAmountOnSlot.getAsBoolean() || currentFluid == null) && cellFluid != null) { if (this.canFillSlot()) { - if (!this.showAmount.getAsBoolean()) { + if (!this.showAmountOnSlot.getAsBoolean()) { cellFluid.amount = 1; } if (this.tank.fill(cellFluid, true) > 0) { @@ -250,14 +324,14 @@ public void tryClickPhantom(MouseData data) { case 1 -> { if (this.canFillSlot()) { if (currentFluid != null) { - if (this.showAmount.getAsBoolean()) { + if (this.showAmountOnSlot.getAsBoolean()) { FluidStack toFill = currentFluid.copy(); toFill.amount = 1000; this.tank.fill(toFill, true); } } else if (this.phantomFluid != null) { FluidStack toFill = this.phantomFluid.copy(); - toFill.amount = this.showAmount.getAsBoolean() ? 1 : toFill.amount; + toFill.amount = this.showAmountOnSlot.getAsBoolean() ? 1 : toFill.amount; this.tank.fill(toFill, true); } } @@ -272,7 +346,7 @@ public void tryClickPhantom(MouseData data) { public void tryScrollPhantom(MouseData mouseData) { FluidStack currentFluid = this.tank.getFluid(); int amount = mouseData.mouseButton; - if (!this.showAmount()) { + if (!this.showAmountOnSlot()) { int newAmt = amount == 1 ? 1 : 0; if (newAmt == 0) { setFluid(null); @@ -293,7 +367,7 @@ public void tryScrollPhantom(MouseData mouseData) { if (currentFluid == null) { if (amount > 0 && this.phantomFluid != null) { FluidStack toFill = this.phantomFluid.copy(); - toFill.amount = this.showAmount() ? amount : 1; + toFill.amount = this.showAmountOnSlot() ? amount : 1; this.tank.fill(toFill, true); } return; diff --git a/src/main/java/gregtech/api/pattern/PatternMatchContext.java b/src/main/java/gregtech/api/pattern/PatternMatchContext.java index f0526f43186..bbbc6326728 100644 --- a/src/main/java/gregtech/api/pattern/PatternMatchContext.java +++ b/src/main/java/gregtech/api/pattern/PatternMatchContext.java @@ -34,6 +34,7 @@ public void increment(String key, int value) { set(key, getOrDefault(key, 0) + value); } + @SuppressWarnings("unchecked") public T getOrDefault(String key, T defaultValue) { return (T) data.getOrDefault(key, defaultValue); } 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/recipes/ingredients/GTRecipeInput.java b/src/main/java/gregtech/api/recipes/ingredients/GTRecipeInput.java index 66eb370b53e..28e53ece72f 100644 --- a/src/main/java/gregtech/api/recipes/ingredients/GTRecipeInput.java +++ b/src/main/java/gregtech/api/recipes/ingredients/GTRecipeInput.java @@ -2,10 +2,13 @@ import gregtech.api.recipes.ingredients.nbtmatch.NBTCondition; import gregtech.api.recipes.ingredients.nbtmatch.NBTMatcher; +import gregtech.api.util.GTLog; import net.minecraft.item.Item; import net.minecraft.item.ItemStack; import net.minecraft.nbt.NBTTagCompound; +import net.minecraft.nbt.NBTTagList; +import net.minecraftforge.common.util.Constants; import net.minecraftforge.fluids.FluidStack; import it.unimi.dsi.fastutil.ints.Int2ObjectMap; @@ -14,9 +17,11 @@ import it.unimi.dsi.fastutil.objects.ObjectLists; import org.jetbrains.annotations.Nullable; +import java.util.Arrays; import java.util.Collection; import java.util.Comparator; import java.util.List; +import java.util.Objects; /** * Definition of ItemStacks, Ore dicts, of ingredients for @@ -202,6 +207,41 @@ public int getSortingOrder() { return this.isNonConsumable() ? SORTING_ORDER_NC : SORTING_ORDER_COMMON; } + public static NBTTagCompound writeToNBT(GTRecipeInput input) { + NBTTagCompound tag = new NBTTagCompound(); + if (input instanceof GTRecipeItemInput) { + NBTTagList stackList = new NBTTagList(); + for (ItemStack stack : input.getInputStacks()) { + stackList.appendTag(stack.serializeNBT()); + } + tag.setTag("stacks", stackList); + } else if (input instanceof GTRecipeOreInput) { + tag.setInteger("ore", input.getOreDict()); + } else if (input instanceof GTRecipeFluidInput) { + tag.setTag("fluid", input.getInputFluidStack().writeToNBT(new NBTTagCompound())); + } + tag.setInteger("amount", input.getAmount()); + return tag; + } + + public static GTRecipeInput readFromNBT(NBTTagCompound tag) { + int amount = tag.getInteger("amount"); + if (tag.hasKey("stacks")) { + NBTTagList list = tag.getTagList("stacks", Constants.NBT.TAG_COMPOUND); + ItemStack[] stacks = new ItemStack[list.tagCount()]; + Arrays.setAll(stacks, i -> new ItemStack(list.getCompoundTagAt(i))); + return new GTRecipeItemInput(stacks, amount); + + } else if (tag.hasKey("ore")) { + return new GTRecipeOreInput(tag.getInteger("ore"), amount); + } else if (tag.hasKey("fluid")) { + FluidStack stack = FluidStack.loadFluidStackFromNBT(tag.getCompoundTag("fluid")); + return new GTRecipeFluidInput(Objects.requireNonNull(stack), amount); + } + GTLog.logger.warn("unable to read tag!: " + tag); + return null; + } + protected static class ItemToMetaList implements Object2ObjectMap.Entry> { protected Item item; diff --git a/src/main/java/gregtech/api/recipes/logic/ParallelLogic.java b/src/main/java/gregtech/api/recipes/logic/ParallelLogic.java index c056890d086..9d853cc19be 100644 --- a/src/main/java/gregtech/api/recipes/logic/ParallelLogic.java +++ b/src/main/java/gregtech/api/recipes/logic/ParallelLogic.java @@ -2,11 +2,11 @@ import gregtech.api.capability.IMultipleTankHandler; import gregtech.api.metatileentity.IVoidable; -import gregtech.api.recipes.FluidKey; import gregtech.api.recipes.Recipe; import gregtech.api.recipes.RecipeBuilder; import gregtech.api.recipes.RecipeMap; import gregtech.api.recipes.ingredients.GTRecipeInput; +import gregtech.api.util.FluidStackHashStrategy; import gregtech.api.util.GTHashMaps; import gregtech.api.util.GTUtility; import gregtech.api.util.ItemStackHashStrategy; @@ -25,7 +25,6 @@ import org.jetbrains.annotations.NotNull; import java.util.Collections; -import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.Set; @@ -46,7 +45,7 @@ public static int getMaxRecipeMultiplier(@NotNull Recipe recipe, @NotNull IItemH Object2IntMap ingredientStacks = GTHashMaps.fromItemHandler(inputs); // Find all the fluids in the combined Fluid Input inventories and create oversized FluidStacks - Map fluidStacks = GTHashMaps.fromFluidHandler(fluidInputs); + Object2IntMap fluidStacks = GTHashMaps.fromFluidHandler(fluidInputs); // Find the maximum number of recipes that can be performed from the items in the item input inventories int itemMultiplier = getMaxRatioItem(ingredientStacks, recipe, parallelAmount); @@ -390,44 +389,42 @@ protected static int getMaxRatioItem(@NotNull Object2IntMap countIngr * @param parallelAmount The limit on the amount of recipes that can be performed at one time * @return The Maximum number of Recipes that can be performed at a single time based on the available Fluids */ - protected static int getMaxRatioFluid(@NotNull Map countFluid, @NotNull Recipe recipe, + protected static int getMaxRatioFluid(@NotNull Object2IntMap countFluid, @NotNull Recipe recipe, int parallelAmount) { int minMultiplier = Integer.MAX_VALUE; // map the recipe input fluids to account for duplicated fluids, // so their sum is counted against the total of fluids available in the input - Map fluidCountMap = new HashMap<>(); - Map notConsumableMap = new HashMap<>(); + Object2IntMap fluidCountMap = GTHashMaps.createFluidStackMap(true); + Object2IntMap notConsumableMap = GTHashMaps.createFluidStackMap(true); for (GTRecipeInput fluidInput : recipe.getFluidInputs()) { int fluidAmount = fluidInput.getAmount(); if (fluidInput.isNonConsumable()) { - notConsumableMap.computeIfPresent(new FluidKey(fluidInput.getInputFluidStack()), - (k, v) -> v + fluidAmount); - notConsumableMap.putIfAbsent(new FluidKey(fluidInput.getInputFluidStack()), fluidAmount); + notConsumableMap.merge(fluidInput.getInputFluidStack(), fluidAmount, Integer::sum); } else { - fluidCountMap.computeIfPresent(new FluidKey(fluidInput.getInputFluidStack()), - (k, v) -> v + fluidAmount); - fluidCountMap.putIfAbsent(new FluidKey(fluidInput.getInputFluidStack()), fluidAmount); + fluidCountMap.merge(fluidInput.getInputFluidStack(), fluidAmount, Integer::sum); } } + FluidStackHashStrategy fluidStrategy = FluidStackHashStrategy.comparingAllButAmount(); + // Iterate through the recipe inputs, excluding the not consumable fluids from the fluid inventory map - for (Map.Entry notConsumableFluid : notConsumableMap.entrySet()) { - int needed = notConsumableFluid.getValue(); + for (FluidStack notConsumableFluid : notConsumableMap.keySet()) { + int needed = notConsumableMap.getInt(notConsumableFluid); int available = 0; // For every fluid gathered from the fluid inputs. - for (Map.Entry inputFluid : countFluid.entrySet()) { + for (FluidStack inputFluid : countFluid.keySet()) { // Strip the Non-consumable tags here, as FluidKey compares the tags, which causes finding matching // fluids // in the input tanks to fail, because there is nothing in those hatches with a non-consumable tag - if (notConsumableFluid.getKey().equals(inputFluid.getKey())) { - available = inputFluid.getValue(); + if (fluidStrategy.equals(notConsumableFluid, inputFluid)) { + available = countFluid.getInt(inputFluid); if (available > needed) { - inputFluid.setValue(available - needed); + countFluid.replace(inputFluid, available - needed); needed -= available; break; } else { - inputFluid.setValue(0); - notConsumableFluid.setValue(needed - available); + countFluid.replace(inputFluid, 0); + notConsumableMap.replace(notConsumableFluid, needed - available); needed -= available; } } @@ -451,13 +448,13 @@ protected static int getMaxRatioFluid(@NotNull Map countFluid } // Iterate through the fluid inputs in the recipe - for (Map.Entry fs : fluidCountMap.entrySet()) { - int needed = fs.getValue(); + for (FluidStack stack : fluidCountMap.keySet()) { + int needed = fluidCountMap.getInt(stack); int available = 0; // For every fluid gathered from the fluid inputs. - for (Map.Entry inputFluid : countFluid.entrySet()) { - if (fs.getKey().equals(inputFluid.getKey())) { - available += inputFluid.getValue(); + for (FluidStack inputFluid : countFluid.keySet()) { + if (fluidStrategy.equals(stack, inputFluid)) { + available += countFluid.getInt(inputFluid); } } if (available >= needed) { 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..e3cc94ac6fe --- /dev/null +++ b/src/main/java/gregtech/api/util/FluidStackHashStrategy.java @@ -0,0 +1,70 @@ +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 Builder builder() { + return new Builder(); + } + + static FluidStackHashStrategy comparingAll() { + return builder().compareFluid(true) + .compareAmount(true) + .compareNBT(true) + .build(); + } + + static FluidStackHashStrategy comparingAllButAmount() { + return builder().compareFluid(true) + .compareNBT(true) + .build(); + } + + class Builder { + + private boolean fluid, amount, nbt; + + public Builder compareFluid(boolean choice) { + this.fluid = choice; + return this; + } + + public Builder compareAmount(boolean choice) { + this.amount = choice; + return this; + } + + public Builder compareNBT(boolean choice) { + this.nbt = choice; + 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/api/util/FluidTooltipUtil.java b/src/main/java/gregtech/api/util/FluidTooltipUtil.java index 5f2ccad8ed9..a4afad4796f 100644 --- a/src/main/java/gregtech/api/util/FluidTooltipUtil.java +++ b/src/main/java/gregtech/api/util/FluidTooltipUtil.java @@ -1,8 +1,10 @@ package gregtech.api.util; +import gregtech.api.GTValues; import gregtech.api.fluids.FluidState; import gregtech.api.fluids.GTFluid; import gregtech.api.unification.material.Material; +import gregtech.client.utils.TooltipHelper; import net.minecraft.client.resources.I18n; import net.minecraft.util.text.TextFormatting; @@ -10,6 +12,8 @@ import net.minecraftforge.fluids.FluidRegistry; import net.minecraftforge.fluids.FluidStack; +import com.cleanroommc.modularui.api.drawable.IKey; +import com.cleanroommc.modularui.screen.RichTooltip; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; @@ -42,9 +46,9 @@ public static void registerTooltip(@NotNull Fluid fluid, @NotNull Supplier getFluidTooltip(Fluid fluid) { + public static @NotNull List getFluidTooltip(@Nullable Fluid fluid) { if (fluid == null) { - return null; + return Collections.emptyList(); } var list = tooltips.get(fluid); @@ -56,29 +60,47 @@ public static List getFluidTooltip(Fluid fluid) { return tooltip; } + public static void handleFluidTooltip(@NotNull RichTooltip tooltip, @Nullable Fluid fluid) { + if (fluid == null) return; + + var tooltipList = tooltips.get(fluid); + if (tooltipList == null) return; + + for (var subList : tooltipList) { + for (String tooltipStr : subList.get()) { + tooltip.addLine(IKey.str(tooltipStr)); + } + } + } + /** * Used to get a Fluid's tooltip. * * @param stack A FluidStack, containing the Fluid to get the tooltip of. * @return The tooltip. */ - public static List getFluidTooltip(FluidStack stack) { + public static @NotNull List getFluidTooltip(@Nullable FluidStack stack) { if (stack == null) { - return null; + return Collections.emptyList(); } return getFluidTooltip(stack.getFluid()); } + public static void handleFluidTooltip(@NotNull RichTooltip tooltip, @Nullable FluidStack stack) { + if (stack == null) return; + handleFluidTooltip(tooltip, stack.getFluid()); + } + /** * Used to get a Fluid's tooltip. * * @param fluidName A String representing a Fluid to get the tooltip of. * @return The tooltip. */ - public static List getFluidTooltip(String fluidName) { + public static @NotNull List getFluidTooltip(@Nullable String fluidName) { if (fluidName == null || fluidName.isEmpty()) { - return null; + return Collections.emptyList(); } return getFluidTooltip(FluidRegistry.getFluid(fluidName)); @@ -110,4 +132,17 @@ public static Supplier> createFluidTooltip(@Nullable Material mater return tooltip; }; } + + public static void addIngotMolFluidTooltip(@NotNull RichTooltip tooltip, @NotNull FluidStack fluidStack) { + // Add tooltip showing how many "ingot moles" (increments of 144) this fluid is if shift is held + if (TooltipHelper.isShiftDown() && fluidStack.amount > GTValues.L) { + int numIngots = fluidStack.amount / GTValues.L; + int extra = fluidStack.amount % GTValues.L; + String fluidAmount = String.format(" %,d L = %,d * %d L", fluidStack.amount, numIngots, GTValues.L); + if (extra != 0) { + fluidAmount += String.format(" + %d L", extra); + } + tooltip.add(TextFormatting.GRAY + LocalizationUtils.format("gregtech.gui.amount_raw") + fluidAmount); + } + } } diff --git a/src/main/java/gregtech/api/util/GTHashMaps.java b/src/main/java/gregtech/api/util/GTHashMaps.java index 9df09cd6368..ce10d6b2a33 100644 --- a/src/main/java/gregtech/api/util/GTHashMaps.java +++ b/src/main/java/gregtech/api/util/GTHashMaps.java @@ -1,14 +1,11 @@ package gregtech.api.util; -import gregtech.api.recipes.FluidKey; - import net.minecraft.item.ItemStack; import net.minecraftforge.fluids.FluidStack; import net.minecraftforge.fluids.capability.IFluidHandler; import net.minecraftforge.items.IItemHandler; import it.unimi.dsi.fastutil.objects.Object2IntLinkedOpenCustomHashMap; -import it.unimi.dsi.fastutil.objects.Object2IntLinkedOpenHashMap; import it.unimi.dsi.fastutil.objects.Object2IntMap; import it.unimi.dsi.fastutil.objects.Object2IntOpenCustomHashMap; import org.jetbrains.annotations.NotNull; @@ -47,8 +44,12 @@ public static Object2IntMap fromItemHandler(@NotNull IItemHandler inp for (int i = 0; i < inputs.getSlots(); i++) { ItemStack stack = inputs.getStackInSlot(i); - if (!stack.isEmpty()) { - map.put(stack.copy(), map.getInt(stack) + stack.getCount()); + if (stack.isEmpty()) continue; + if (map.containsKey(stack)) { + map.merge(stack, stack.getCount(), Integer::sum); + } else { + ItemStack key = GTUtility.copy(1, stack); + map.put(key, stack.getCount()); } } @@ -83,37 +84,69 @@ public static Object2IntMap fromItemStackCollection(@NotNull Iterable // Create a single stack of the combined count for each item for (ItemStack stack : inputs) { - if (!stack.isEmpty()) { - map.put(stack.copy(), map.getInt(stack) + stack.getCount()); + if (stack.isEmpty()) continue; + if (map.containsKey(stack)) { + map.merge(stack, stack.getCount(), Integer::sum); + } else { + map.put(GTUtility.copy(1, stack), stack.getCount()); } } return map; } + /** + * @param linked if the map should respect the order that keys are added + * @return an Object2IntMap + */ @NotNull - private static Object2IntMap createItemStackMap(boolean linked) { + public static Object2IntMap createItemStackMap(boolean linked) { ItemStackHashStrategy strategy = ItemStackHashStrategy.comparingAllButCount(); return linked ? new Object2IntLinkedOpenCustomHashMap<>(strategy) : new Object2IntOpenCustomHashMap<>(strategy); } /** - * Maps all fluids in the {@link IFluidHandler} into a {@link FluidKey}, {@link Integer} value as amount + * @param linked if the map should respect the order that keys are added + * @return an Object2IntMap + */ + @NotNull + public static Object2IntMap createFluidStackMap(boolean linked) { + var strategy = FluidStackHashStrategy.comparingAllButAmount(); + return linked ? new Object2IntLinkedOpenCustomHashMap<>(strategy) : new Object2IntOpenCustomHashMap<>(strategy); + } + + /** + * Maps all fluids in the {@link IFluidHandler} into a {@link FluidStack}, {@link Integer} value as amount + * + * @param fluidInputs The combined fluid input inventory handler, in the form of an {@link IFluidHandler} + * @return a {@link Set} of unique {@link FluidStack}s for each fluid in the handler. Will be oversized stacks if + * required + */ + public static Object2IntMap fromFluidHandler(IFluidHandler fluidInputs) { + return fromFluidHandler(fluidInputs, true); + } + + /** + * Maps all fluids in the {@link IFluidHandler} into a {@link FluidStack}, {@link Integer} value as amount * * @param fluidInputs The combined fluid input inventory handler, in the form of an {@link IFluidHandler} - * @return a {@link Set} of unique {@link FluidKey}s for each fluid in the handler. Will be oversized stacks if + * @return a {@link Set} of unique {@link FluidStack}s for each fluid in the handler. Will be oversized stacks if * required */ - public static Map fromFluidHandler(IFluidHandler fluidInputs) { - final Object2IntMap map = new Object2IntLinkedOpenHashMap<>(); + public static Object2IntMap fromFluidHandler(IFluidHandler fluidInputs, boolean linked) { + final Object2IntMap map = createFluidStackMap(linked); // Create a single stack of the combined count for each item - for (int i = 0; i < fluidInputs.getTankProperties().length; i++) { - FluidStack fluidStack = fluidInputs.getTankProperties()[i].getContents(); - if (fluidStack != null && fluidStack.amount > 0) { - FluidKey key = new FluidKey(fluidStack); - map.put(key, map.getInt(key) + fluidStack.amount); + for (var prop : fluidInputs.getTankProperties()) { + FluidStack fluidStack = prop.getContents(); + if (GTUtility.isEmpty(fluidStack)) + continue; + + if (map.containsKey(fluidStack)) { + map.merge(fluidStack, fluidStack.amount, Integer::sum); + } else { + map.put(GTUtility.copy(1, fluidStack), fluidStack.amount); } } @@ -121,22 +154,38 @@ public static Map fromFluidHandler(IFluidHandler fluidInputs) } /** - * Maps all fluids in the {@link FluidStack} {@link Collection} into a {@link FluidKey}, {@link Integer} value as + * Maps all fluids in the {@link FluidStack} {@link Collection} into a {@link FluidStack}, {@link Integer} value as * amount * * @param fluidInputs The combined fluid input inventory handler, in the form of an {@link IFluidHandler} - * @return a {@link Set} of unique {@link FluidKey}s for each fluid in the handler. Will be oversized stacks if + * @return a {@link Set} of unique {@link FluidStack}s for each fluid in the handler. Will be oversized stacks if * required */ - public static Map fromFluidCollection(Collection fluidInputs) { - final Object2IntMap map = new Object2IntLinkedOpenHashMap<>(); + public static Object2IntMap fromFluidCollection(Collection fluidInputs) { + return fromFluidCollection(fluidInputs, true); + } + + /** + * Maps all fluids in the {@link FluidStack} {@link Collection} into a {@link FluidStack}, {@link Integer} value as + * amount + * + * @param fluidInputs The combined fluid input inventory handler, in the form of an {@link IFluidHandler} + * @return a {@link Set} of unique {@link FluidStack}s for each fluid in the handler. Will be oversized stacks if + * required + */ + public static Object2IntMap fromFluidCollection(Collection fluidInputs, boolean linked) { + final Object2IntMap map = createFluidStackMap(linked); // Create a single stack of the combined count for each item for (FluidStack fluidStack : fluidInputs) { - if (fluidStack != null && fluidStack.amount > 0) { - FluidKey key = new FluidKey(fluidStack); - map.put(key, map.getInt(key) + fluidStack.amount); + if (GTUtility.isEmpty(fluidStack)) + continue; + + if (map.containsKey(fluidStack)) { + map.merge(fluidStack, fluidStack.amount, Integer::sum); + } else { + map.put(GTUtility.copy(1, fluidStack), fluidStack.amount); } } diff --git a/src/main/java/gregtech/api/util/GTLambdaUtils.java b/src/main/java/gregtech/api/util/GTLambdaUtils.java new file mode 100644 index 00000000000..5adca846fb6 --- /dev/null +++ b/src/main/java/gregtech/api/util/GTLambdaUtils.java @@ -0,0 +1,13 @@ +package gregtech.api.util; + +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import java.util.function.Consumer; + +public class GTLambdaUtils { + + public static @NotNull Consumer mergeConsumers(@Nullable Consumer first, @NotNull Consumer andThen) { + return first == null ? andThen : first.andThen(andThen); + } +} diff --git a/src/main/java/gregtech/api/util/GTTransferUtils.java b/src/main/java/gregtech/api/util/GTTransferUtils.java index d79024f844b..f444c78aa73 100644 --- a/src/main/java/gregtech/api/util/GTTransferUtils.java +++ b/src/main/java/gregtech/api/util/GTTransferUtils.java @@ -43,7 +43,7 @@ public static int transferFluids(@NotNull IFluidHandler sourceHandler, @NotNull currentFluid.amount = fluidLeftToTransfer; FluidStack fluidStack = sourceHandler.drain(currentFluid, false); - if (fluidStack == null || fluidStack.amount == 0) { + if (GTUtility.isEmpty(fluidStack)) { continue; } @@ -51,7 +51,7 @@ public static int transferFluids(@NotNull IFluidHandler sourceHandler, @NotNull if (canInsertAmount > 0) { fluidStack.amount = canInsertAmount; fluidStack = sourceHandler.drain(fluidStack, true); - if (fluidStack != null && fluidStack.amount > 0) { + if (!GTUtility.isEmpty(fluidStack)) { destHandler.fill(fluidStack, true); fluidLeftToTransfer -= fluidStack.amount; @@ -68,13 +68,13 @@ public static boolean transferExactFluidStack(@NotNull IFluidHandler sourceHandl @NotNull IFluidHandler destHandler, FluidStack fluidStack) { int amount = fluidStack.amount; FluidStack sourceFluid = sourceHandler.drain(fluidStack, false); - if (sourceFluid == null || sourceFluid.amount != amount) { + if (GTUtility.isEmpty(sourceFluid) || sourceFluid.amount != amount) { return false; } int canInsertAmount = destHandler.fill(sourceFluid, false); if (canInsertAmount == amount) { sourceFluid = sourceHandler.drain(sourceFluid, true); - if (sourceFluid != null && sourceFluid.amount > 0) { + if (!GTUtility.isEmpty(sourceFluid)) { destHandler.fill(sourceFluid, true); return true; } diff --git a/src/main/java/gregtech/api/util/GTUtility.java b/src/main/java/gregtech/api/util/GTUtility.java index 287c1a76c5a..a4f44bb2bce 100644 --- a/src/main/java/gregtech/api/util/GTUtility.java +++ b/src/main/java/gregtech/api/util/GTUtility.java @@ -6,7 +6,6 @@ import gregtech.api.capability.IMultipleTankHandler; import gregtech.api.cover.CoverDefinition; import gregtech.api.fluids.GTFluid; -import gregtech.api.gui.widgets.ProgressWidget; import gregtech.api.items.behavior.CoverItemBehavior; import gregtech.api.items.metaitem.MetaItem; import gregtech.api.items.metaitem.stats.IItemBehaviour; @@ -20,6 +19,7 @@ import gregtech.api.unification.OreDictUnifier; import gregtech.api.unification.ore.OrePrefix; import gregtech.api.unification.stack.ItemAndMetadata; +import gregtech.api.util.function.impl.TimedProgressSupplier; import net.minecraft.block.BlockRedstoneWire; import net.minecraft.block.BlockSnow; @@ -59,6 +59,7 @@ import it.unimi.dsi.fastutil.objects.ObjectOpenCustomHashSet; import org.apache.commons.lang3.ArrayUtils; import org.apache.commons.lang3.tuple.Pair; +import org.jetbrains.annotations.ApiStatus; import org.jetbrains.annotations.Contract; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; @@ -508,6 +509,40 @@ public static ItemStack copy(int newCount, @NotNull ItemStack stack) { return copy; } + /** + * Copies the FluidStack with new stack size. + * + * @param stack item stack for copying + * @return a copy of FluidStack, or {@code null} if the stack is empty + */ + @Nullable + public static FluidStack copy(int newCount, @Nullable FluidStack stack) { + if (isEmpty(stack)) return null; + FluidStack copy = stack.copy(); + copy.amount = newCount; + return copy; + } + + /** + * Copies the FluidStack. + * + * @param stack fluid stack for copying + * @return a copy of FluidStack, or {@code null} if the stack is empty + */ + @Nullable + public static FluidStack copy(@Nullable FluidStack stack) { + if (isEmpty(stack)) return null; + return stack.copy(); + } + + /** + * @param stack fluid stack to check if empty + * @return true if the stack is null or amount is <= 0 + */ + public static boolean isEmpty(FluidStack stack) { + return stack == null || stack.amount <= 0; + } + /** * Copies first non-empty ItemStack from stacks. * @@ -878,6 +913,8 @@ public static ResourceLocation gregtechId(@NotNull String path) { } @Contract("null -> null") + @Deprecated + @ApiStatus.ScheduledForRemoval(inVersion = "2.10") public static TextComponentTranslation getFluidTranslation(@Nullable FluidStack stack) { if (stack == null) return null; if (stack.getFluid() instanceof GTFluid.GTMaterialFluid materialFluid) { @@ -888,6 +925,8 @@ public static TextComponentTranslation getFluidTranslation(@Nullable FluidStack } @Contract("null -> null") + @Deprecated + @ApiStatus.ScheduledForRemoval(inVersion = "2.10") public static TextComponentTranslation getFluidTranslation(@Nullable Fluid fluid) { if (fluid == null) return null; if (fluid instanceof GTFluid.GTMaterialFluid materialFluid) { @@ -896,10 +935,11 @@ public static TextComponentTranslation getFluidTranslation(@Nullable Fluid fluid return new TextComponentTranslation(fluid.getUnlocalizedName()); } + @Deprecated public static @NotNull Pair createPairedSupplier(int ticksPerCycle, int width, double splitPoint) { AtomicDouble tracker = new AtomicDouble(0.0); - DoubleSupplier supplier1 = new ProgressWidget.TimedProgressSupplier(ticksPerCycle, width, false) { + DoubleSupplier supplier1 = new TimedProgressSupplier(ticksPerCycle, width, false) { @Override public double getAsDouble() { diff --git a/src/main/java/gregtech/api/util/ItemStackHashStrategy.java b/src/main/java/gregtech/api/util/ItemStackHashStrategy.java index 405bcaf842e..20e12330ea9 100644 --- a/src/main/java/gregtech/api/util/ItemStackHashStrategy.java +++ b/src/main/java/gregtech/api/util/ItemStackHashStrategy.java @@ -16,8 +16,8 @@ public interface ItemStackHashStrategy extends Hash.Strategy { /** * @return a builder object for producing a custom ItemStackHashStrategy. */ - static ItemStackHashStrategyBuilder builder() { - return new ItemStackHashStrategyBuilder(); + static Builder builder() { + return new Builder(); } /** @@ -56,7 +56,7 @@ static ItemStackHashStrategy comparingItemDamageCount() { /** * Builder pattern class for generating customized ItemStackHashStrategy */ - class ItemStackHashStrategyBuilder { + class Builder { private boolean item, count, damage, tag, meta; @@ -66,7 +66,7 @@ class ItemStackHashStrategyBuilder { * @param choice {@code true} to consider this property, {@code false} to ignore it. * @return {@code this} */ - public ItemStackHashStrategyBuilder compareItem(boolean choice) { + public Builder compareItem(boolean choice) { item = choice; return this; } @@ -77,7 +77,7 @@ public ItemStackHashStrategyBuilder compareItem(boolean choice) { * @param choice {@code true} to consider this property, {@code false} to ignore it. * @return {@code this} */ - public ItemStackHashStrategyBuilder compareCount(boolean choice) { + public Builder compareCount(boolean choice) { count = choice; return this; } @@ -88,7 +88,7 @@ public ItemStackHashStrategyBuilder compareCount(boolean choice) { * @param choice {@code true} to consider this property, {@code false} to ignore it. * @return {@code this} */ - public ItemStackHashStrategyBuilder compareDamage(boolean choice) { + public Builder compareDamage(boolean choice) { damage = choice; return this; } @@ -99,7 +99,7 @@ public ItemStackHashStrategyBuilder compareDamage(boolean choice) { * @param choice {@code true} to consider this property, {@code false} to ignore it. * @return {@code this} */ - public ItemStackHashStrategyBuilder compareMetadata(boolean choice) { + public Builder compareMetadata(boolean choice) { meta = choice; return this; } @@ -110,7 +110,7 @@ public ItemStackHashStrategyBuilder compareMetadata(boolean choice) { * @param choice {@code true} to consider this property, {@code false} to ignore it. * @return {@code this} */ - public ItemStackHashStrategyBuilder compareTag(boolean choice) { + public Builder compareTag(boolean choice) { tag = choice; return this; } diff --git a/src/main/java/gregtech/api/util/KeyUtil.java b/src/main/java/gregtech/api/util/KeyUtil.java new file mode 100644 index 00000000000..1b547e2fa69 --- /dev/null +++ b/src/main/java/gregtech/api/util/KeyUtil.java @@ -0,0 +1,148 @@ +package gregtech.api.util; + +import gregtech.api.GTValues; +import gregtech.api.fluids.GTFluid; + +import net.minecraft.util.text.TextFormatting; +import net.minecraftforge.fluids.Fluid; +import net.minecraftforge.fluids.FluidStack; + +import com.cleanroommc.modularui.api.drawable.IDrawable; +import com.cleanroommc.modularui.api.drawable.IKey; +import org.apache.commons.lang3.ArrayUtils; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import java.util.Arrays; +import java.util.function.LongSupplier; +import java.util.function.Supplier; + +public class KeyUtil { + + public static IKey string(String s) { + return IKey.str(s); + } + + public static IKey string(Supplier s) { + return IKey.dynamic(s); + } + + public static IKey string(TextFormatting formatting, String string) { + if (string == null) return IKey.EMPTY; + return IKey.str(string).style(formatting); + } + + // maybe enforce using keys for args in lang/string keys since they format correctly + public static IKey string(TextFormatting formatting, String string, Object... args) { + if (string == null) return IKey.EMPTY; + return IKey.str(string, args).style(formatting); + } + + public static IKey string(TextFormatting formatting, Supplier stringSupplier) { + return IKey.dynamic(stringSupplier).style(formatting); + } + + public static IKey string(TextFormatting formatting, Supplier stringSupplier, + Supplier argSupplier) { + return IKey.dynamic(() -> String.format(stringSupplier.get(), argSupplier.get())).style(formatting); + } + + public static IKey string(Supplier formatting, String s) { + return IKey.dynamic(() -> IKey.str(s).style(formatting.get()).getFormatted()); + } + + public static IKey string(Supplier formatting, Supplier stringSupplier) { + return IKey.dynamic(() -> IKey.str(stringSupplier.get()).style(formatting.get()).getFormatted()); + } + + public static IKey lang(String lang, Object... args) { + return IKey.lang(lang, args); + } + + public static IKey lang(TextFormatting formatting, String lang, Object... args) { + return IKey.lang(lang, args).style(formatting); + } + + public static IKey lang(TextFormatting formatting, String lang, Supplier argSupplier) { + return IKey.lang(lang, argSupplier).style(formatting); + } + + public static IKey lang(Supplier formatting, String lang, Supplier argSupplier) { + return IKey.dynamic(() -> lang(lang, argSupplier.get()).style(formatting.get()).getFormatted()); + } + + public static IKey number(long number) { + return string(TextFormattingUtil.formatNumbers(number)); + } + + public static IKey number(TextFormatting formatting, long number) { + return number(number).style(formatting); + } + + public static IKey number(TextFormatting formatting, long number, String suffix) { + return string(formatting, TextFormattingUtil.formatNumbers(number) + suffix); + } + + public static IKey number(TextFormatting formatting, LongSupplier supplier) { + return string(formatting, () -> TextFormattingUtil.formatNumbers(supplier.getAsLong())); + } + + public static IKey number(TextFormatting formatting, LongSupplier supplier, String suffix) { + return string(formatting, () -> TextFormattingUtil.formatNumbers(supplier.getAsLong()) + suffix); + } + + public static IKey number(Supplier formatting, LongSupplier supplier) { + return string(formatting, () -> TextFormattingUtil.formatNumbers(supplier.getAsLong())); + } + + public static IKey number(Supplier formatting, long number) { + return string(formatting, () -> TextFormattingUtil.formatNumbers(number)); + } + + public static IKey number(Supplier formatting, long number, String suffix) { + return string(formatting, () -> TextFormattingUtil.formatNumbers(number) + suffix); + } + + public static IKey number(Supplier formatting, LongSupplier supplier, String suffix) { + return string(formatting, () -> TextFormattingUtil.formatNumbers(supplier.getAsLong()) + suffix); + } + + public static IDrawable setHover(IKey body, IDrawable... hover) { + if (ArrayUtils.isEmpty(hover)) return body; + if (!GTValues.isClientSide()) return IDrawable.NONE; + return body.asTextIcon() + .asHoverable() + .addTooltipDrawableLines(Arrays.asList(hover)); + } + + @NotNull + public static IKey fluid(TextFormatting formatting, FluidStack fluid) { + return fluid(fluid.getFluid(), fluid).style(formatting); + } + + @NotNull + public static IKey fluid(TextFormatting formatting, Fluid fluid) { + return fluid(fluid).style(formatting); + } + + @NotNull + public static IKey fluid(@Nullable FluidStack fluid) { + if (fluid == null) return IKey.EMPTY; + return fluid(fluid.getFluid(), fluid); + } + + @NotNull + public static IKey fluid(@Nullable Fluid fluid) { + return fluid(fluid, null); + } + + @NotNull + public static IKey fluid(@Nullable Fluid fluid, @Nullable FluidStack stack) { + if (fluid == null) return IKey.EMPTY; + if (fluid instanceof GTFluid.GTMaterialFluid gtFluid) { + return gtFluid.getLocalizedKey(); + } + if (stack == null) return IKey.lang(fluid.getUnlocalizedName()); + else return IKey.lang(fluid.getUnlocalizedName(stack)); + } +} diff --git a/src/main/java/gregtech/api/util/NetworkUtil.java b/src/main/java/gregtech/api/util/NetworkUtil.java new file mode 100644 index 00000000000..e85773c4d5a --- /dev/null +++ b/src/main/java/gregtech/api/util/NetworkUtil.java @@ -0,0 +1,108 @@ +package gregtech.api.util; + +import net.minecraft.item.ItemStack; +import net.minecraft.nbt.NBTTagCompound; +import net.minecraft.network.PacketBuffer; +import net.minecraftforge.fluids.Fluid; +import net.minecraftforge.fluids.FluidRegistry; +import net.minecraftforge.fluids.FluidStack; + +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import java.io.IOException; + +public class NetworkUtil { + + /** + * Write a {@link FluidStack} to a {@link PacketBuffer}. + * + * @param to the buffer to write to + * @param stack the stack to write + */ + public static void writeFluidStack(@NotNull PacketBuffer to, @Nullable FluidStack stack) { + NBTTagCompound tag = new NBTTagCompound(); + if (stack == null) { + to.writeBoolean(false); + } else { + to.writeBoolean(true); + stack.writeToNBT(tag); + } + to.writeCompoundTag(tag); + } + + /** + * Read a {@link FluidStack} from a {@link PacketBuffer} + * + * @param from the packet buffer to read from + * @return the decoded fluid stack + */ + public static @Nullable FluidStack readFluidStack(@NotNull PacketBuffer from) { + if (from.readBoolean()) { + NBTTagCompound tag; + try { + tag = from.readCompoundTag(); + } catch (IOException e) { + GTLog.logger.error("Exception reading a FluidStack from a PacketBuffer!", e); + return null; + } + return FluidStack.loadFluidStackFromNBT(tag); + } else { + return null; + } + } + + /** + * Write a {@link Fluid} to a {@link PacketBuffer}. + * + * @param to the buffer to write to + * @param fluid the fluid to write + */ + public static void writeFluid(@NotNull PacketBuffer to, @Nullable Fluid fluid) { + if (fluid == null) { + to.writeBoolean(false); + } else { + to.writeBoolean(true); + to.writeString(fluid.getName()); + } + } + + /** + * Read a {@link Fluid} from a {@link PacketBuffer} + * + * @param from the packet buffer to read from + * @return the decoded fluid + */ + public static @Nullable Fluid readFluid(@NotNull PacketBuffer from) { + if (from.readBoolean()) { + return FluidRegistry.getFluid(from.readString(Short.MAX_VALUE)); + } else { + return null; + } + } + + /** + * Write an {@link ItemStack} to a {@link PacketBuffer}. + * + * @param to the buffer to write to + * @param stack the stack to write + */ + public static void writeItemStack(@NotNull PacketBuffer to, @NotNull ItemStack stack) { + to.writeItemStack(stack); + } + + /** + * Read an {@link ItemStack} from a {@link PacketBuffer} + * + * @param from the packet buffer to read from + * @return the decoded item stack + */ + public static @NotNull ItemStack readItemStack(@NotNull PacketBuffer from) { + try { + return from.readItemStack(); + } catch (IOException e) { + GTLog.logger.error("Exception reading an ItemStack from a PacketBuffer!", e); + return ItemStack.EMPTY; + } + } +} diff --git a/src/main/java/gregtech/api/util/TextFormattingUtil.java b/src/main/java/gregtech/api/util/TextFormattingUtil.java index ba3e3606f43..cbf02c18bf4 100644 --- a/src/main/java/gregtech/api/util/TextFormattingUtil.java +++ b/src/main/java/gregtech/api/util/TextFormattingUtil.java @@ -1,5 +1,6 @@ package gregtech.api.util; +import java.math.BigInteger; import java.text.NumberFormat; public class TextFormattingUtil { @@ -13,9 +14,23 @@ public class TextFormattingUtil { 1_000_000_000_000_000_000L }; + private static final BigInteger[] metricBigSuffixValues = { + BigInteger.TEN.pow(3), + BigInteger.TEN.pow(6), + BigInteger.TEN.pow(9), + BigInteger.TEN.pow(12), + BigInteger.TEN.pow(15), + BigInteger.TEN.pow(18), + BigInteger.TEN.pow(21), + BigInteger.TEN.pow(24), + BigInteger.TEN.pow(27), + BigInteger.TEN.pow(30) + }; + private static final char[] metricSuffixChars = { - 'k', 'M', 'G', 'T', 'P', 'E' + 'k', 'M', 'G', 'T', 'P', 'E', 'Z', 'Y', 'R', 'Q' }; + private static final NumberFormat NUMBER_FORMAT = NumberFormat.getInstance(); public static String formatLongToCompactString(long value, int precision) { @@ -36,13 +51,36 @@ public static String formatLongToCompactString(long value, int precision) { long suffixValue = metricSuffixValues[i]; stb.append(value / suffixValue); - long truncatedDigit = value % suffixValue / (suffixValue / 10); + long truncatedDigit = value % suffixValue / (suffixValue / (long) Math.pow(10, precision - 3)); if (truncatedDigit > 0) { stb.append('.').append(truncatedDigit); } return stb.append(metricSuffixChars[i]).toString(); } + public static String formatBigIntToCompactString(BigInteger value, int precision) { + if (BigInteger.ZERO.equals(value) || value.abs().compareTo(BigInteger.TEN.pow(precision)) < 0) { + return value.toString(); // deal with easy case + } + + StringBuilder stb = new StringBuilder(); + if (value.signum() == -1) { + stb.append('-'); + value = value.abs(); + } + + int c = 0; + while (value.compareTo(metricBigSuffixValues[c]) >= 0) { + c++; + } + + return stb.append(value.divide(metricBigSuffixValues[c - 1])) + .append('.') + .append(value.toString(), 4, precision + 1) + .append(metricSuffixChars[c - 1]) + .toString(); + } + public static String formatLongToCompactString(long value) { return formatLongToCompactString(value, 3); } diff --git a/src/main/java/gregtech/api/util/function/ByteSupplier.java b/src/main/java/gregtech/api/util/function/ByteSupplier.java new file mode 100644 index 00000000000..1d23eb6c0d7 --- /dev/null +++ b/src/main/java/gregtech/api/util/function/ByteSupplier.java @@ -0,0 +1,14 @@ +package gregtech.api.util.function; + +import java.util.function.Supplier; + +@FunctionalInterface +public interface ByteSupplier extends Supplier { + + @Override + default Byte get() { + return getByte(); + } + + byte getByte(); +} diff --git a/src/main/java/gregtech/api/util/function/FloatSupplier.java b/src/main/java/gregtech/api/util/function/FloatSupplier.java new file mode 100644 index 00000000000..4d4501c4778 --- /dev/null +++ b/src/main/java/gregtech/api/util/function/FloatSupplier.java @@ -0,0 +1,14 @@ +package gregtech.api.util.function; + +import java.util.function.Supplier; + +@FunctionalInterface +public interface FloatSupplier extends Supplier { + + @Override + default Float get() { + return getFloat(); + } + + float getFloat(); +} diff --git a/src/main/java/gregtech/api/util/function/impl/TimedProgressSupplier.java b/src/main/java/gregtech/api/util/function/impl/TimedProgressSupplier.java new file mode 100644 index 00000000000..80346348221 --- /dev/null +++ b/src/main/java/gregtech/api/util/function/impl/TimedProgressSupplier.java @@ -0,0 +1,37 @@ +package gregtech.api.util.function.impl; + +import java.util.function.DoubleSupplier; + +public class TimedProgressSupplier implements DoubleSupplier { + + private final int msPerCycle; + private final int maxValue; + private final boolean countDown; + private long startTime; + + public TimedProgressSupplier(int ticksPerCycle, int maxValue, boolean countDown) { + this.msPerCycle = ticksPerCycle * 50; + this.maxValue = maxValue; + this.countDown = countDown; + this.startTime = System.currentTimeMillis(); + } + + public void resetCountdown() { + startTime = System.currentTimeMillis(); + } + + @Override + public double getAsDouble() { + return calculateTime(); + } + + private double calculateTime() { + long currentTime = System.currentTimeMillis(); + long msPassed = (currentTime - startTime) % msPerCycle; + double currentValue = 1.0 * msPassed * maxValue / msPerCycle; + if (countDown) { + return (maxValue - currentValue) / maxValue; + } + return currentValue / maxValue; + } +} diff --git a/src/main/java/gregtech/common/covers/filter/SimpleFluidFilter.java b/src/main/java/gregtech/common/covers/filter/SimpleFluidFilter.java index f91d5105799..25bbbf64362 100644 --- a/src/main/java/gregtech/common/covers/filter/SimpleFluidFilter.java +++ b/src/main/java/gregtech/common/covers/filter/SimpleFluidFilter.java @@ -60,7 +60,8 @@ public void configureFilterTanks(int amount) { .key('F', i -> new GTFluidSlot() .syncHandler(GTFluidSlot.sync(filterReader.getFluidTank(i)) .phantom(true) - .showAmount(getFilterReader()::shouldShowAmount))) + .showAmountOnSlot(getFilterReader()::shouldShowAmount) + .showAmountInTooltip(getFilterReader()::shouldShowAmount))) .build().marginRight(4)) .child(createBlacklistUI()); } diff --git a/src/main/java/gregtech/common/items/behaviors/DataItemBehavior.java b/src/main/java/gregtech/common/items/behaviors/DataItemBehavior.java index 116dca7ee5d..6930abe4f28 100644 --- a/src/main/java/gregtech/common/items/behaviors/DataItemBehavior.java +++ b/src/main/java/gregtech/common/items/behaviors/DataItemBehavior.java @@ -38,8 +38,12 @@ public boolean requireDataBank() { public void addInformation(@NotNull ItemStack itemStack, List lines) { String researchId = AssemblyLineManager.readResearchId(itemStack); if (researchId == null) return; + collectResearchItems(researchId, lines); + } + + public static void collectResearchItems(String id, List lines) { Collection recipes = ((IResearchRecipeMap) RecipeMaps.ASSEMBLY_LINE_RECIPES) - .getDataStickEntry(researchId); + .getDataStickEntry(id); if (recipes != null && !recipes.isEmpty()) { lines.add(I18n.format("behavior.data_item.assemblyline.title")); Collection added = new ObjectOpenCustomHashSet<>(ItemStackHashStrategy.comparingAllButCount()); diff --git a/src/main/java/gregtech/common/metatileentities/multi/MetaTileEntityCokeOven.java b/src/main/java/gregtech/common/metatileentities/multi/MetaTileEntityCokeOven.java index 781ae14b7e3..72021b7cf61 100644 --- a/src/main/java/gregtech/common/metatileentities/multi/MetaTileEntityCokeOven.java +++ b/src/main/java/gregtech/common/metatileentities/multi/MetaTileEntityCokeOven.java @@ -1,14 +1,14 @@ package gregtech.common.metatileentities.multi; import gregtech.api.GTValues; -import gregtech.api.gui.GuiTextures; -import gregtech.api.gui.ModularUI; -import gregtech.api.gui.widgets.*; import gregtech.api.metatileentity.MetaTileEntity; import gregtech.api.metatileentity.MetaTileEntityUIFactory; import gregtech.api.metatileentity.interfaces.IGregTechTileEntity; import gregtech.api.metatileentity.multiblock.IMultiblockPart; import gregtech.api.metatileentity.multiblock.RecipeMapPrimitiveMultiblockController; +import gregtech.api.metatileentity.multiblock.ui.MultiblockUIFactory; +import gregtech.api.mui.GTGuiTextures; +import gregtech.api.mui.GTGuiTheme; import gregtech.api.pattern.BlockPattern; import gregtech.api.pattern.FactoryBlockPattern; import gregtech.api.recipes.RecipeMaps; @@ -19,6 +19,7 @@ import gregtech.common.blocks.BlockMetalCasing; import gregtech.common.blocks.MetaBlocks; import gregtech.common.metatileentities.MetaTileEntities; +import gregtech.common.mui.widget.GTFluidSlot; import net.minecraft.block.state.IBlockState; import net.minecraft.entity.player.EntityPlayer; @@ -35,6 +36,12 @@ import codechicken.lib.render.CCRenderState; import codechicken.lib.render.pipeline.IVertexOperation; import codechicken.lib.vec.Matrix4; +import com.cleanroommc.modularui.api.drawable.IKey; +import com.cleanroommc.modularui.utils.Alignment; +import com.cleanroommc.modularui.value.sync.DoubleSyncValue; +import com.cleanroommc.modularui.widgets.ItemSlot; +import com.cleanroommc.modularui.widgets.ProgressWidget; +import com.cleanroommc.modularui.widgets.slot.ModularSlot; import org.jetbrains.annotations.NotNull; public class MetaTileEntityCokeOven extends RecipeMapPrimitiveMultiblockController { @@ -93,22 +100,42 @@ public boolean hasMaintenanceMechanics() { } @Override - protected ModularUI.Builder createUITemplate(EntityPlayer entityPlayer) { - return ModularUI.builder(GuiTextures.PRIMITIVE_BACKGROUND, 176, 166) - .shouldColor(false) - .widget(new LabelWidget(5, 5, getMetaFullName())) - .widget(new SlotWidget(importItems, 0, 52, 30, true, true) - .setBackgroundTexture(GuiTextures.PRIMITIVE_SLOT, GuiTextures.PRIMITIVE_FURNACE_OVERLAY)) - .widget(new RecipeProgressWidget(recipeMapWorkable::getProgressPercent, 76, 32, 20, 15, - GuiTextures.PRIMITIVE_BLAST_FURNACE_PROGRESS_BAR, ProgressWidget.MoveType.HORIZONTAL, - RecipeMaps.COKE_OVEN_RECIPES)) - .widget(new SlotWidget(exportItems, 0, 103, 30, true, false) - .setBackgroundTexture(GuiTextures.PRIMITIVE_SLOT, GuiTextures.PRIMITIVE_FURNACE_OVERLAY)) - .widget(new TankWidget(exportFluids.getTankAt(0), 134, 13, 20, 58) - .setBackgroundTexture(GuiTextures.PRIMITIVE_LARGE_FLUID_TANK) - .setOverlayTexture(GuiTextures.PRIMITIVE_LARGE_FLUID_TANK_OVERLAY) - .setContainerClicking(true, false)) - .bindPlayerInventory(entityPlayer.inventory, GuiTextures.PRIMITIVE_SLOT, 0); + protected MultiblockUIFactory createUIFactory() { + return new MultiblockUIFactory(this) + .disableButtons() + .disableDisplay() + .setSize(176, 166) + .addScreenChildren((parent, syncManager) -> { + parent.child(IKey.lang(getMetaFullName()).asWidget().pos(5, 5)) + .child(new ItemSlot() + .slot(new ModularSlot(importItems, 0) + .singletonSlotGroup()) + .pos(52, 30)) + .child(new ProgressWidget() + .texture(GTGuiTextures.PRIMITIVE_BLAST_FURNACE_PROGRESS_BAR, -1) + .size(20, 15) + .pos(76, 32) + .value(new DoubleSyncValue(recipeMapWorkable::getProgressPercent))) + .child(new ItemSlot() + .slot(new ModularSlot(exportItems, 0) + .accessibility(false, true)) + .pos(103, 30)) + .child(new GTFluidSlot() + .overlay(GTGuiTextures.PRIMITIVE_LARGE_FLUID_TANK_OVERLAY.asIcon() + .alignment(Alignment.CenterLeft) + .marginLeft(1)) + .syncHandler(GTFluidSlot.sync(exportFluids.getTankAt(0)) + .drawAlwaysFull(false) + .showAmountOnSlot(false) + .accessibility(true, false)) + .pos(134, 13) + .size(20, 58)); + }); + } + + @Override + public GTGuiTheme getUITheme() { + return GTGuiTheme.PRIMITIVE; } @Override diff --git a/src/main/java/gregtech/common/metatileentities/multi/MetaTileEntityLargeBoiler.java b/src/main/java/gregtech/common/metatileentities/multi/MetaTileEntityLargeBoiler.java index 6921ca44d5f..da5afd69fdc 100644 --- a/src/main/java/gregtech/common/metatileentities/multi/MetaTileEntityLargeBoiler.java +++ b/src/main/java/gregtech/common/metatileentities/multi/MetaTileEntityLargeBoiler.java @@ -1,24 +1,26 @@ package gregtech.common.metatileentities.multi; +import gregtech.api.capability.IControllable; import gregtech.api.capability.impl.BoilerRecipeLogic; import gregtech.api.capability.impl.CommonFluidFilters; import gregtech.api.capability.impl.FluidTankList; import gregtech.api.capability.impl.ItemHandlerList; -import gregtech.api.gui.GuiTextures; -import gregtech.api.gui.Widget; -import gregtech.api.gui.Widget.ClickData; -import gregtech.api.gui.resources.TextureArea; -import gregtech.api.gui.widgets.ClickButtonWidget; -import gregtech.api.gui.widgets.WidgetGroup; import gregtech.api.metatileentity.MTETrait; import gregtech.api.metatileentity.MetaTileEntity; import gregtech.api.metatileentity.interfaces.IGregTechTileEntity; import gregtech.api.metatileentity.multiblock.*; +import gregtech.api.metatileentity.multiblock.ui.KeyManager; +import gregtech.api.metatileentity.multiblock.ui.MultiblockUIBuilder; +import gregtech.api.metatileentity.multiblock.ui.MultiblockUIFactory; +import gregtech.api.metatileentity.multiblock.ui.TemplateBarBuilder; +import gregtech.api.metatileentity.multiblock.ui.UISyncer; +import gregtech.api.mui.GTGuiTextures; +import gregtech.api.mui.GTGuiTheme; +import gregtech.api.mui.GTGuis; import gregtech.api.pattern.BlockPattern; import gregtech.api.pattern.FactoryBlockPattern; import gregtech.api.pattern.PatternMatchContext; -import gregtech.api.util.TextComponentUtil; -import gregtech.api.util.TextFormattingUtil; +import gregtech.api.util.KeyUtil; import gregtech.client.renderer.ICubeRenderer; import gregtech.client.utils.TooltipHelper; import gregtech.core.sound.GTSoundEvents; @@ -29,8 +31,6 @@ import net.minecraft.network.PacketBuffer; import net.minecraft.util.ResourceLocation; import net.minecraft.util.SoundEvent; -import net.minecraft.util.math.MathHelper; -import net.minecraft.util.text.ITextComponent; import net.minecraft.util.text.TextFormatting; import net.minecraft.world.World; import net.minecraftforge.fluids.IFluidTank; @@ -41,13 +41,30 @@ import codechicken.lib.render.CCRenderState; import codechicken.lib.render.pipeline.IVertexOperation; import codechicken.lib.vec.Matrix4; +import com.cleanroommc.modularui.api.GuiAxis; +import com.cleanroommc.modularui.api.IPanelHandler; +import com.cleanroommc.modularui.api.drawable.IKey; +import com.cleanroommc.modularui.drawable.ItemDrawable; +import com.cleanroommc.modularui.drawable.Rectangle; +import com.cleanroommc.modularui.screen.ModularPanel; +import com.cleanroommc.modularui.utils.Color; +import com.cleanroommc.modularui.value.sync.DoubleSyncValue; +import com.cleanroommc.modularui.value.sync.IntSyncValue; +import com.cleanroommc.modularui.value.sync.PanelSyncManager; +import com.cleanroommc.modularui.value.sync.StringSyncValue; +import com.cleanroommc.modularui.widgets.ButtonWidget; +import com.cleanroommc.modularui.widgets.SliderWidget; +import com.cleanroommc.modularui.widgets.layout.Flow; +import com.cleanroommc.modularui.widgets.textfield.TextFieldWidget; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import java.util.Collections; import java.util.List; +import java.util.function.UnaryOperator; -public class MetaTileEntityLargeBoiler extends MultiblockWithDisplayBase implements IProgressBarMultiblock { +public class MetaTileEntityLargeBoiler extends MultiblockWithDisplayBase implements ProgressBarMultiblock, + IControllable { public final BoilerType boilerType; protected BoilerRecipeLogic recipeLogic; @@ -95,46 +112,6 @@ private void resetTileAbilities() { this.steamOutputTank = new FluidTankList(true); } - @Override - protected void addDisplayText(List textList) { - MultiblockDisplayText.builder(textList, isStructureFormed()) - .setWorkingStatus(recipeLogic.isWorkingEnabled(), recipeLogic.isActive()) - .addCustom(tl -> { - if (isStructureFormed()) { - // Steam Output line - ITextComponent steamOutput = TextComponentUtil.stringWithColor( - TextFormatting.AQUA, - TextFormattingUtil.formatNumbers(recipeLogic.getLastTickSteam()) + " L/t"); - - tl.add(TextComponentUtil.translationWithColor( - TextFormatting.GRAY, - "gregtech.multiblock.large_boiler.steam_output", - steamOutput)); - - // Efficiency line - ITextComponent efficiency = TextComponentUtil.stringWithColor( - getNumberColor(recipeLogic.getHeatScaled()), - recipeLogic.getHeatScaled() + "%"); - - tl.add(TextComponentUtil.translationWithColor( - TextFormatting.GRAY, - "gregtech.multiblock.large_boiler.efficiency", - efficiency)); - - // Throttle line - ITextComponent throttle = TextComponentUtil.stringWithColor( - getNumberColor(getThrottle()), - getThrottle() + "%"); - - tl.add(TextComponentUtil.translationWithColor( - TextFormatting.GRAY, - "gregtech.multiblock.large_boiler.throttle", - throttle)); - } - }) - .addWorkingStatusLine(); - } - private TextFormatting getNumberColor(int number) { if (number == 0) { return TextFormatting.DARK_RED; @@ -148,37 +125,153 @@ private TextFormatting getNumberColor(int number) { } @Override - protected void addWarningText(List textList) { - super.addWarningText(textList); - if (isStructureFormed()) { - int[] waterAmount = getWaterAmount(); - if (waterAmount[0] == 0) { - textList.add(TextComponentUtil.translationWithColor(TextFormatting.YELLOW, + protected void configureDisplayText(MultiblockUIBuilder builder) { + builder.setWorkingStatus(recipeLogic.isWorkingEnabled(), recipeLogic.isActive()) + .addCustom(this::addCustomData) + .addWorkingStatusLine(); + } + + @Override + protected void configureWarningText(MultiblockUIBuilder builder) { + super.configureWarningText(builder); + builder.addCustom((manager, syncer) -> { + if (isStructureFormed() && syncer.syncBoolean(getWaterFilled() == 0)) { + manager.add(KeyUtil.lang(TextFormatting.YELLOW, "gregtech.multiblock.large_boiler.no_water")); - textList.add(TextComponentUtil.translationWithColor(TextFormatting.GRAY, + manager.add(KeyUtil.lang(TextFormatting.GRAY, "gregtech.multiblock.large_boiler.explosion_tooltip")); } - } + }); } @Override - protected @NotNull Widget getFlexButton(int x, int y, int width, int height) { - WidgetGroup group = new WidgetGroup(x, y, width, height); - group.addWidget(new ClickButtonWidget(0, 0, 9, 18, "", this::decrementThrottle) - .setButtonTexture(GuiTextures.BUTTON_THROTTLE_MINUS) - .setTooltipText("gregtech.multiblock.large_boiler.throttle_decrement")); - group.addWidget(new ClickButtonWidget(9, 0, 9, 18, "", this::incrementThrottle) - .setButtonTexture(GuiTextures.BUTTON_THROTTLE_PLUS) - .setTooltipText("gregtech.multiblock.large_boiler.throttle_increment")); - return group; + public GTGuiTheme getUITheme() { + return switch (this.boilerType) { + case BRONZE -> GTGuiTheme.BRONZE; + case STEEL -> GTGuiTheme.STEEL; + default -> super.getUITheme(); + }; } - private void incrementThrottle(ClickData clickData) { - this.throttlePercentage = MathHelper.clamp(throttlePercentage + 5, 25, 100); + @Override + protected MultiblockUIFactory createUIFactory() { + return super.createUIFactory() + .createFlexButton((guiData, syncManager) -> { + var throttle = syncManager.panel("throttle_panel", this::makeThrottlePanel, true); + + return new ButtonWidget<>() + .size(18) + .overlay(GTGuiTextures.FILTER_SETTINGS_OVERLAY.asIcon().size(16)) + .addTooltipLine(IKey.lang("gregtech.multiblock.large_boiler.throttle_button.tooltip")) + .onMousePressed(i -> { + if (throttle.isPanelOpen()) { + throttle.closePanel(); + } else { + throttle.openPanel(); + } + return true; + }); + }); + } + + private void addCustomData(KeyManager keyManager, UISyncer syncer) { + if (isStructureFormed()) { + int steam = syncer.syncInt(recipeLogic.getLastTickSteam()); + int heatScaled = syncer.syncInt(recipeLogic.getHeatScaled()); + int throttleAmt = syncer.syncInt(getThrottle()); + + // Steam Output line + IKey steamOutput = KeyUtil.number(TextFormatting.AQUA, + steam, " L/t"); + + keyManager.add(KeyUtil.lang(TextFormatting.GRAY, + "gregtech.multiblock.large_boiler.steam_output", steamOutput)); + + // Efficiency line + IKey efficiency = KeyUtil.number( + getNumberColor(heatScaled), heatScaled, "%"); + keyManager.add(KeyUtil.lang(TextFormatting.GRAY, + "gregtech.multiblock.large_boiler.efficiency", efficiency)); + + // Throttle line + IKey throttle = KeyUtil.number( + getNumberColor(throttleAmt), + throttleAmt, "%"); + keyManager.add(KeyUtil.lang(TextFormatting.GRAY, + "gregtech.multiblock.large_boiler.throttle", throttle)); + } } - private void decrementThrottle(ClickData clickData) { - this.throttlePercentage = MathHelper.clamp(throttlePercentage - 5, 25, 100); + private ModularPanel makeThrottlePanel(PanelSyncManager syncManager, IPanelHandler syncHandler) { + StringSyncValue throttleValue = new StringSyncValue(() -> throttlePercentage + "%", str -> { + try { + if (str.charAt(str.length() - 1) == '%') { + str = str.substring(0, str.length() - 1); + } + + this.throttlePercentage = Integer.parseInt(str); + } catch (NumberFormatException ignored) { + + } + }); + DoubleSyncValue sliderValue = new DoubleSyncValue( + () -> (double) getThrottlePercentage() / 100, + d -> setThrottlePercentage((int) (d * 100))); + + return GTGuis.createPopupPanel("boiler_throttle", 116, 53) + .child(Flow.row() + .pos(4, 4) + .height(16) + .coverChildrenWidth() + .child(new ItemDrawable(getStackForm()) + .asWidget() + .size(16) + .marginRight(4)) + .child(IKey.lang("gregtech.multiblock.large_boiler.throttle.title") + .asWidget() + .heightRel(1.0f))) + .child(Flow.row() + .top(20) + .margin(4, 0) + .coverChildrenHeight() + .child(new SliderWidget() + .background(new Rectangle().setColor(Color.BLACK.brighter(2)).asIcon() + .height(8)) + .bounds(0, 1) + .setAxis(GuiAxis.X) + .value(sliderValue) + .widthRel(0.7f) + .height(20)) + // todo switch this text field with GTTextFieldWidget in PR #2700 + .child(new TextFieldWidget() + .widthRel(0.3f) + .height(20) + // TODO proper color + .setTextColor(Color.WHITE.darker(1)) + .setValidator(str -> { + if (str.charAt(str.length() - 1) == '%') { + str = str.substring(0, str.length() - 1); + } + + try { + long l = Long.parseLong(str); + if (l < 0) l = 0; + else if (l > 100) l = 100; + return String.valueOf(l); + } catch (NumberFormatException ignored) { + return throttleValue.getValue(); + } + }) + .value(throttleValue) + .background(GTGuiTextures.DISPLAY))); + } + + private void setThrottlePercentage(int amount) { + this.throttlePercentage = amount; + } + + private int getThrottlePercentage() { + return this.throttlePercentage; } @Override @@ -187,7 +280,7 @@ public boolean isActive() { } @Override - protected BlockPattern createStructurePattern() { + protected @NotNull BlockPattern createStructurePattern() { return FactoryBlockPattern.start() .aisle("XXX", "CCC", "CCC", "CCC") .aisle("XXX", "CPC", "CPC", "CCC") @@ -311,61 +404,79 @@ protected boolean shouldUpdate(MTETrait trait) { } @Override - protected boolean shouldShowVoidingModeButton() { + public boolean shouldShowVoidingModeButton() { return false; } @Override - public double getFillPercentage(int index) { - if (!isStructureFormed()) return 0; - int[] waterAmount = getWaterAmount(); - if (waterAmount[1] == 0) return 0; // no water capacity - return (1.0 * waterAmount[0]) / waterAmount[1]; + public int getProgressBarCount() { + return 1; } @Override - public TextureArea getProgressBarTexture(int index) { - return GuiTextures.PROGRESS_BAR_FLUID_RIG_DEPLETION; + public void registerBars(List> bars, PanelSyncManager syncManager) { + IntSyncValue waterFilledValue = new IntSyncValue(this::getWaterFilled); + IntSyncValue waterCapacityValue = new IntSyncValue(this::getWaterCapacity); + syncManager.syncValue("water_filled", waterFilledValue); + syncManager.syncValue("water_capacity", waterCapacityValue); + + bars.add(barTest -> barTest + .progress(() -> waterCapacityValue.getIntValue() == 0 ? 0 : + waterFilledValue.getIntValue() * 1.0 / waterCapacityValue.getIntValue()) + .texture(GTGuiTextures.PROGRESS_BAR_FLUID_RIG_DEPLETION) + .tooltipBuilder(tooltip -> { + if (isStructureFormed()) { + if (waterFilledValue.getIntValue() == 0) { + tooltip.addLine(IKey.lang("gregtech.multiblock.large_boiler.no_water")); + } else { + tooltip.addLine(IKey.lang("gregtech.multiblock.large_boiler.water_bar_hover", + waterFilledValue.getIntValue(), waterCapacityValue.getIntValue())); + } + } else { + tooltip.addLine(IKey.lang("gregtech.multiblock.invalid_structure")); + } + })); } - @Override - public void addBarHoverText(List hoverList, int index) { - if (!isStructureFormed()) { - hoverList.add(TextComponentUtil.translationWithColor(TextFormatting.GRAY, - "gregtech.multiblock.invalid_structure")); - } else { - int[] waterAmount = getWaterAmount(); - if (waterAmount[0] == 0) { - hoverList.add(TextComponentUtil.translationWithColor(TextFormatting.YELLOW, - "gregtech.multiblock.large_boiler.no_water")); - } else { - ITextComponent waterInfo = TextComponentUtil.translationWithColor( - TextFormatting.BLUE, - "%s / %s L", - waterAmount[0], waterAmount[1]); - hoverList.add(TextComponentUtil.translationWithColor( - TextFormatting.GRAY, - "gregtech.multiblock.large_boiler.water_bar_hover", - waterInfo)); + /** + * @return the total amount of water filling the inputs + */ + private int getWaterFilled() { + if (!isStructureFormed()) return 0; + List tanks = getAbilities(MultiblockAbility.IMPORT_FLUIDS); + int filled = 0; + for (IFluidTank tank : tanks) { + if (tank == null || tank.getFluid() == null) continue; + if (CommonFluidFilters.BOILER_FLUID.test(tank.getFluid())) { + filled += tank.getFluidAmount(); } } + return filled; } /** - * Returns an int[] of {AmountFilled, Capacity} where capacity is the sum of hatches with some water in them. - * If there is no water in the boiler (or the structure isn't formed, both of these values will be zero. + * @return the total capacity for water-containing inputs */ - private int[] getWaterAmount() { - if (!isStructureFormed()) return new int[] { 0, 0 }; + private int getWaterCapacity() { + if (!isStructureFormed()) return 0; List tanks = getAbilities(MultiblockAbility.IMPORT_FLUIDS); - int filled = 0, capacity = 0; + int capacity = 0; for (IFluidTank tank : tanks) { if (tank == null || tank.getFluid() == null) continue; if (CommonFluidFilters.BOILER_FLUID.test(tank.getFluid())) { - filled += tank.getFluidAmount(); capacity += tank.getCapacity(); } } - return new int[] { filled, capacity }; + return capacity; + } + + @Override + public boolean isWorkingEnabled() { + return recipeLogic.isWorkingEnabled(); + } + + @Override + public void setWorkingEnabled(boolean isWorkingAllowed) { + recipeLogic.setWorkingEnabled(isWorkingAllowed); } } diff --git a/src/main/java/gregtech/common/metatileentities/multi/MetaTileEntityMultiblockTank.java b/src/main/java/gregtech/common/metatileentities/multi/MetaTileEntityMultiblockTank.java index eeabc2cd762..d717903228e 100644 --- a/src/main/java/gregtech/common/metatileentities/multi/MetaTileEntityMultiblockTank.java +++ b/src/main/java/gregtech/common/metatileentities/multi/MetaTileEntityMultiblockTank.java @@ -3,14 +3,13 @@ import gregtech.api.capability.impl.FilteredFluidHandler; import gregtech.api.capability.impl.FluidTankList; import gregtech.api.capability.impl.PropertyFluidFilter; -import gregtech.api.gui.GuiTextures; -import gregtech.api.gui.ModularUI; -import gregtech.api.gui.widgets.LabelWidget; -import gregtech.api.gui.widgets.TankWidget; import gregtech.api.metatileentity.MetaTileEntity; import gregtech.api.metatileentity.interfaces.IGregTechTileEntity; import gregtech.api.metatileentity.multiblock.IMultiblockPart; import gregtech.api.metatileentity.multiblock.MultiblockWithDisplayBase; +import gregtech.api.metatileentity.multiblock.ui.MultiblockUIFactory; +import gregtech.api.mui.GTGuiTextures; +import gregtech.api.mui.GTGuiTheme; import gregtech.api.pattern.BlockPattern; import gregtech.api.pattern.FactoryBlockPattern; import gregtech.client.renderer.ICubeRenderer; @@ -19,6 +18,7 @@ import gregtech.common.blocks.BlockSteamCasing; import gregtech.common.blocks.MetaBlocks; import gregtech.common.metatileentities.MetaTileEntities; +import gregtech.common.mui.widget.GTFluidSlot; import net.minecraft.block.state.IBlockState; import net.minecraft.client.resources.I18n; @@ -37,6 +37,9 @@ import codechicken.lib.render.CCRenderState; import codechicken.lib.render.pipeline.IVertexOperation; import codechicken.lib.vec.Matrix4; +import com.cleanroommc.modularui.api.drawable.IKey; +import com.cleanroommc.modularui.utils.Alignment; +import com.cleanroommc.modularui.widgets.TextWidget; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; @@ -110,6 +113,12 @@ public ICubeRenderer getBaseTexture(IMultiblockPart sourcePart) { return Textures.WOOD_WALL; } + @Override + public GTGuiTheme getUITheme() { + if (isMetal) return GTGuiTheme.STEEL; + else return GTGuiTheme.PRIMITIVE; + } + @Override public boolean hasMaintenanceMechanics() { return false; @@ -129,13 +138,25 @@ protected boolean openGUIOnRightClick() { } @Override - protected ModularUI.Builder createUITemplate(@NotNull EntityPlayer entityPlayer) { - return ModularUI.defaultBuilder() - .widget(new LabelWidget(6, 6, getMetaFullName())) - .widget(new TankWidget(importFluids.getTankAt(0), 52, 18, 72, 61) - .setBackgroundTexture(GuiTextures.SLOT) - .setContainerClicking(true, true)) - .bindPlayerInventory(entityPlayer.inventory, GuiTextures.SLOT, 0); + protected MultiblockUIFactory createUIFactory() { + return new MultiblockUIFactory(this) + .setSize(176, 166) + .disableDisplay() + .disableButtons() + .addScreenChildren((parent, syncManager) -> { + parent.child(new TextWidget(IKey.lang(getMetaFullName())) + .pos(5, 5)); + parent.child(new GTFluidSlot() + .pos(52, 18) + .size(72, 61) + // todo this looks ugly + .overlay(GTGuiTextures.PRIMITIVE_LARGE_FLUID_TANK_OVERLAY.asIcon() + .alignment(Alignment.CenterLeft) + .size(30, 58)) + .syncHandler(GTFluidSlot.sync(importFluids.getTankAt(0)) + .showAmountOnSlot(false) + .drawAlwaysFull(false))); + }); } @Override diff --git a/src/main/java/gregtech/common/metatileentities/multi/MetaTileEntityPrimitiveBlastFurnace.java b/src/main/java/gregtech/common/metatileentities/multi/MetaTileEntityPrimitiveBlastFurnace.java index 90078eaf604..b7dd899205f 100644 --- a/src/main/java/gregtech/common/metatileentities/multi/MetaTileEntityPrimitiveBlastFurnace.java +++ b/src/main/java/gregtech/common/metatileentities/multi/MetaTileEntityPrimitiveBlastFurnace.java @@ -1,16 +1,13 @@ package gregtech.common.metatileentities.multi; import gregtech.api.GTValues; -import gregtech.api.gui.GuiTextures; -import gregtech.api.gui.ModularUI; -import gregtech.api.gui.widgets.LabelWidget; -import gregtech.api.gui.widgets.ProgressWidget; -import gregtech.api.gui.widgets.RecipeProgressWidget; -import gregtech.api.gui.widgets.SlotWidget; import gregtech.api.metatileentity.MetaTileEntity; import gregtech.api.metatileentity.interfaces.IGregTechTileEntity; import gregtech.api.metatileentity.multiblock.IMultiblockPart; import gregtech.api.metatileentity.multiblock.RecipeMapPrimitiveMultiblockController; +import gregtech.api.metatileentity.multiblock.ui.MultiblockUIFactory; +import gregtech.api.mui.GTGuiTextures; +import gregtech.api.mui.GTGuiTheme; import gregtech.api.pattern.BlockPattern; import gregtech.api.pattern.FactoryBlockPattern; import gregtech.api.pattern.TraceabilityPredicate; @@ -29,7 +26,6 @@ import net.minecraft.block.state.IBlockState; import net.minecraft.entity.EntityLivingBase; -import net.minecraft.entity.player.EntityPlayer; import net.minecraft.init.SoundEvents; import net.minecraft.util.*; import net.minecraft.util.math.AxisAlignedBB; @@ -42,6 +38,15 @@ import codechicken.lib.texture.TextureUtils; import codechicken.lib.vec.Cuboid6; import codechicken.lib.vec.Matrix4; +import com.cleanroommc.modularui.api.drawable.IKey; +import com.cleanroommc.modularui.drawable.UITexture; +import com.cleanroommc.modularui.utils.Alignment; +import com.cleanroommc.modularui.value.sync.DoubleSyncValue; +import com.cleanroommc.modularui.widgets.ItemSlot; +import com.cleanroommc.modularui.widgets.layout.Flow; +import com.cleanroommc.modularui.widgets.layout.Grid; +import com.cleanroommc.modularui.widgets.slot.ModularSlot; +import com.cleanroommc.modularui.widgets.slot.SlotGroup; import org.apache.commons.lang3.ArrayUtils; import org.jetbrains.annotations.NotNull; @@ -81,26 +86,59 @@ public ICubeRenderer getBaseTexture(IMultiblockPart sourcePart) { } @Override - protected ModularUI.Builder createUITemplate(EntityPlayer entityPlayer) { - return ModularUI.builder(GuiTextures.PRIMITIVE_BACKGROUND, 176, 166) - .shouldColor(false) - .widget(new LabelWidget(5, 5, getMetaFullName())) - .widget(new SlotWidget(importItems, 0, 52, 20, true, true) - .setBackgroundTexture(GuiTextures.PRIMITIVE_SLOT, GuiTextures.PRIMITIVE_INGOT_OVERLAY)) - .widget(new SlotWidget(importItems, 1, 52, 38, true, true) - .setBackgroundTexture(GuiTextures.PRIMITIVE_SLOT, GuiTextures.PRIMITIVE_DUST_OVERLAY)) - .widget(new SlotWidget(importItems, 2, 52, 56, true, true) - .setBackgroundTexture(GuiTextures.PRIMITIVE_SLOT, GuiTextures.PRIMITIVE_FURNACE_OVERLAY)) - .widget(new RecipeProgressWidget(recipeMapWorkable::getProgressPercent, 77, 39, 20, 15, - GuiTextures.PRIMITIVE_BLAST_FURNACE_PROGRESS_BAR, ProgressWidget.MoveType.HORIZONTAL, - RecipeMaps.PRIMITIVE_BLAST_FURNACE_RECIPES)) - .widget(new SlotWidget(exportItems, 0, 104, 38, true, false) - .setBackgroundTexture(GuiTextures.PRIMITIVE_SLOT, GuiTextures.PRIMITIVE_INGOT_OVERLAY)) - .widget(new SlotWidget(exportItems, 1, 122, 38, true, false) - .setBackgroundTexture(GuiTextures.PRIMITIVE_SLOT, GuiTextures.PRIMITIVE_DUST_OVERLAY)) - .widget(new SlotWidget(exportItems, 2, 140, 38, true, false) - .setBackgroundTexture(GuiTextures.PRIMITIVE_SLOT, GuiTextures.PRIMITIVE_DUST_OVERLAY)) - .bindPlayerInventory(entityPlayer.inventory, GuiTextures.PRIMITIVE_SLOT, 0); + protected MultiblockUIFactory createUIFactory() { + return new MultiblockUIFactory(this) + .setSize(176, 166) + .disableDisplay() + .disableButtons() + .addScreenChildren((parent, syncManager) -> { + UITexture[] importOverlays = { + GTGuiTextures.PRIMITIVE_INGOT_OVERLAY, + GTGuiTextures.PRIMITIVE_DUST_OVERLAY, + GTGuiTextures.PRIMITIVE_FURNACE_OVERLAY + }; + + UITexture[] exportOverlays = { + GTGuiTextures.PRIMITIVE_INGOT_OVERLAY, + GTGuiTextures.PRIMITIVE_DUST_OVERLAY, + GTGuiTextures.PRIMITIVE_DUST_OVERLAY + }; + + SlotGroup importGroup = new SlotGroup("import", 1, true); + + parent.child(IKey.lang(getMetaFullName()).asWidget().pos(5, 5)) + .child(Flow.row() + .top(20) + .alignX(0.5f) + // .pos(52, 20) + .crossAxisAlignment(Alignment.CrossAxis.CENTER) + .coverChildren() + .child(new Grid() + .coverChildren() + .mapTo(1, 3, value -> new ItemSlot() + .background(GTGuiTextures.SLOT_PRIMITIVE, importOverlays[value]) + .slot(new ModularSlot(importItems, value) + .slotGroup(importGroup))) + .marginRight(6)) + .child(new com.cleanroommc.modularui.widgets.ProgressWidget() + // .pos(77, 39) + .size(20, 15) + .marginRight(6) + .texture(GTGuiTextures.PRIMITIVE_BLAST_FURNACE_PROGRESS_BAR, 20) + .value(new DoubleSyncValue(recipeMapWorkable::getProgressPercent))) + .child(new Grid() + .coverChildren() + // .pos(104, 38) + .mapTo(3, 3, value -> new ItemSlot() + .background(GTGuiTextures.SLOT_PRIMITIVE, exportOverlays[value]) + .slot(new ModularSlot(exportItems, value) + .accessibility(false, true))))); + }); + } + + @Override + public GTGuiTheme getUITheme() { + return GTGuiTheme.PRIMITIVE; } @Override diff --git a/src/main/java/gregtech/common/metatileentities/multi/MetaTileEntityPumpHatch.java b/src/main/java/gregtech/common/metatileentities/multi/MetaTileEntityPumpHatch.java index a17b955729c..a251b2f2a46 100644 --- a/src/main/java/gregtech/common/metatileentities/multi/MetaTileEntityPumpHatch.java +++ b/src/main/java/gregtech/common/metatileentities/multi/MetaTileEntityPumpHatch.java @@ -128,7 +128,7 @@ public ModularPanel buildUI(PosGuiData guiData, PanelSyncManager guiSyncManager) guiSyncManager.registerSlotGroup("item_inv", 2); GTFluidSyncHandler tankSyncHandler = GTFluidSlot.sync(this.exportFluids.getTankAt(0)) - .showAmount(false) + .showAmountOnSlot(false) .accessibility(true, false); // TODO: Change the position of the name when it's standardized. diff --git a/src/main/java/gregtech/common/metatileentities/multi/electric/MetaTileEntityActiveTransformer.java b/src/main/java/gregtech/common/metatileentities/multi/electric/MetaTileEntityActiveTransformer.java index d22e681fad3..ad4f72c2e5f 100644 --- a/src/main/java/gregtech/common/metatileentities/multi/electric/MetaTileEntityActiveTransformer.java +++ b/src/main/java/gregtech/common/metatileentities/multi/electric/MetaTileEntityActiveTransformer.java @@ -9,14 +9,13 @@ import gregtech.api.metatileentity.interfaces.IGregTechTileEntity; import gregtech.api.metatileentity.multiblock.IMultiblockPart; import gregtech.api.metatileentity.multiblock.MultiblockAbility; -import gregtech.api.metatileentity.multiblock.MultiblockDisplayText; import gregtech.api.metatileentity.multiblock.MultiblockWithDisplayBase; +import gregtech.api.metatileentity.multiblock.ui.MultiblockUIBuilder; import gregtech.api.pattern.BlockPattern; import gregtech.api.pattern.FactoryBlockPattern; import gregtech.api.pattern.PatternMatchContext; import gregtech.api.pattern.TraceabilityPredicate; -import gregtech.api.util.TextComponentUtil; -import gregtech.api.util.TextFormattingUtil; +import gregtech.api.util.KeyUtil; import gregtech.client.renderer.ICubeRenderer; import gregtech.client.renderer.texture.Textures; import gregtech.client.utils.TooltipHelper; @@ -31,7 +30,6 @@ import net.minecraft.network.PacketBuffer; import net.minecraft.util.EnumFacing; import net.minecraft.util.ResourceLocation; -import net.minecraft.util.text.ITextComponent; import net.minecraft.util.text.TextFormatting; import net.minecraft.world.World; import net.minecraftforge.common.capabilities.Capability; @@ -41,6 +39,7 @@ import codechicken.lib.render.CCRenderState; import codechicken.lib.render.pipeline.IVertexOperation; import codechicken.lib.vec.Matrix4; +import com.cleanroommc.modularui.api.drawable.IKey; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; @@ -164,51 +163,39 @@ public void renderMetaTileEntity(CCRenderState renderState, Matrix4 translation, } @Override - protected void addDisplayText(List textList) { - MultiblockDisplayText.builder(textList, isStructureFormed()) - .setWorkingStatus(true, isActive()) // set to true because we only want a two-state system (running or - // not running) + protected void configureDisplayText(MultiblockUIBuilder builder) { + builder.setWorkingStatus(true, isActive()) .setWorkingStatusKeys( "gregtech.multiblock.idling", "gregtech.multiblock.idling", "gregtech.machine.active_transformer.routing") - .addWorkingStatusLine() - .addCustom(tl -> { + .addCustom((list, syncer) -> { if (isStructureFormed()) { // Max input line - ITextComponent maxInputFormatted = TextComponentUtil.stringWithColor( - TextFormatting.WHITE, - TextFormattingUtil.formatNumbers( - powerInput.getInputVoltage() * powerInput.getInputAmperage()) + " EU/t"); - tl.add(TextComponentUtil.translationWithColor( - TextFormatting.GREEN, - "gregtech.multiblock.active_transformer.max_in", + IKey maxInputFormatted = KeyUtil.number(TextFormatting.WHITE, + syncer.syncLong(powerInput.getInputVoltage() * powerInput.getInputAmperage()), " EU/t"); + list.add(KeyUtil.lang(TextFormatting.GREEN, "gregtech.multiblock.active_transformer.max_in", maxInputFormatted)); // Max output line - ITextComponent maxOutputFormatted = TextComponentUtil.stringWithColor( - TextFormatting.WHITE, - TextFormattingUtil.formatNumbers( - powerOutput.getOutputVoltage() * powerOutput.getOutputAmperage()) + " EU/t"); - tl.add(TextComponentUtil.translationWithColor( - TextFormatting.RED, - "gregtech.multiblock.active_transformer.max_out", + IKey maxOutputFormatted = KeyUtil.number(TextFormatting.WHITE, + syncer.syncLong(powerOutput.getOutputVoltage() * powerOutput.getOutputAmperage()), + " EU/t"); + list.add(KeyUtil.lang(TextFormatting.RED, "gregtech.multiblock.active_transformer.max_out", maxOutputFormatted)); // Average I/O line - ITextComponent avgInputFormatted = TextComponentUtil.stringWithColor( - TextFormatting.WHITE, - TextFormattingUtil.formatNumbers(averageIOLastSec) + " EU/t"); - tl.add(TextComponentUtil.translationWithColor( - TextFormatting.AQUA, - "gregtech.multiblock.active_transformer.average_io", - avgInputFormatted)); + IKey avgIOFormatted = KeyUtil.number(TextFormatting.WHITE, syncer.syncLong(averageIOLastSec), + " EU/t"); + list.add(KeyUtil.lang(TextFormatting.AQUA, "gregtech.multiblock.active_transformer.average_io", + avgIOFormatted)); } - }); + }) + .addWorkingStatusLine(); } @Override - protected boolean shouldShowVoidingModeButton() { + public boolean shouldShowVoidingModeButton() { return false; } 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 99f1d89726e..8d83c4b148e 100644 --- a/src/main/java/gregtech/common/metatileentities/multi/electric/MetaTileEntityCleanroom.java +++ b/src/main/java/gregtech/common/metatileentities/multi/electric/MetaTileEntityCleanroom.java @@ -20,8 +20,8 @@ import gregtech.api.metatileentity.multiblock.ICleanroomReceiver; import gregtech.api.metatileentity.multiblock.IMultiblockPart; import gregtech.api.metatileentity.multiblock.MultiblockAbility; -import gregtech.api.metatileentity.multiblock.MultiblockDisplayText; import gregtech.api.metatileentity.multiblock.MultiblockWithDisplayBase; +import gregtech.api.metatileentity.multiblock.ui.MultiblockUIBuilder; import gregtech.api.pattern.BlockPattern; import gregtech.api.pattern.FactoryBlockPattern; import gregtech.api.pattern.MultiblockShapeInfo; @@ -30,8 +30,8 @@ import gregtech.api.pattern.TraceabilityPredicate; import gregtech.api.util.BlockInfo; import gregtech.api.util.GTUtility; +import gregtech.api.util.KeyUtil; import gregtech.api.util.Mods; -import gregtech.api.util.TextComponentUtil; import gregtech.client.renderer.ICubeRenderer; import gregtech.client.renderer.texture.Textures; import gregtech.client.utils.TooltipHelper; @@ -62,7 +62,6 @@ import net.minecraft.util.math.BlockPos; import net.minecraft.util.math.MathHelper; import net.minecraft.util.text.ITextComponent; -import net.minecraft.util.text.TextComponentString; import net.minecraft.util.text.TextComponentTranslation; import net.minecraft.util.text.TextFormatting; import net.minecraft.world.World; @@ -75,6 +74,7 @@ import codechicken.lib.render.CCRenderState; import codechicken.lib.render.pipeline.IVertexOperation; import codechicken.lib.vec.Matrix4; +import com.cleanroommc.modularui.api.drawable.IKey; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; @@ -484,57 +484,51 @@ protected boolean isMachineBanned(MetaTileEntity metaTileEntity) { } @Override - protected void addDisplayText(List textList) { - MultiblockDisplayText.builder(textList, isStructureFormed()) - .setWorkingStatus(cleanroomLogic.isWorkingEnabled(), cleanroomLogic.isActive()) + protected void configureDisplayText(MultiblockUIBuilder builder) { + builder.setWorkingStatus(cleanroomLogic.isWorkingEnabled(), cleanroomLogic.isActive()) .addEnergyUsageLine(energyContainer) - .addCustom(tl -> { + .addEnergyUsageExactLine(isClean() ? 4 : GTValues.VA[getEnergyTier()]) + .addCustom((list, syncer) -> { // Cleanliness status line if (isStructureFormed()) { - ITextComponent cleanState; - if (isClean()) { - cleanState = TextComponentUtil.translationWithColor( - TextFormatting.GREEN, - "gregtech.multiblock.cleanroom.clean_state", - this.cleanAmount); + IKey cleanState; + int amount = syncer.syncInt(cleanAmount); + if (amount >= CLEAN_AMOUNT_THRESHOLD) { + cleanState = KeyUtil.lang(TextFormatting.GREEN, + "gregtech.multiblock.cleanroom.clean_state", amount); } else { - cleanState = TextComponentUtil.translationWithColor( - TextFormatting.DARK_RED, - "gregtech.multiblock.cleanroom.dirty_state", - this.cleanAmount); + cleanState = KeyUtil.lang(TextFormatting.DARK_RED, + "gregtech.multiblock.cleanroom.dirty_state", amount); } - tl.add(TextComponentUtil.translationWithColor( - TextFormatting.GRAY, - "gregtech.multiblock.cleanroom.clean_status", + list.add(KeyUtil.lang(TextFormatting.GRAY, "gregtech.multiblock.cleanroom.clean_status", cleanState)); } }) - .addCustom(tl -> { - if (!cleanroomLogic.isVoltageHighEnough()) { - ITextComponent energyNeeded = new TextComponentString( - GTValues.VNF[cleanroomFilter.getMinTier()]); - tl.add(TextComponentUtil.translationWithColor(TextFormatting.YELLOW, - "gregtech.multiblock.cleanroom.low_tier", energyNeeded)); - } - }) - .addEnergyUsageExactLine(isClean() ? 4 : GTValues.VA[getEnergyTier()]) - .addWorkingStatusLine() - .addProgressLine(getProgressPercent() / 100.0); + .addProgressLine(getProgress(), getMaxProgress()) + .addWorkingStatusLine(); } @Override - protected void addWarningText(List textList) { - MultiblockDisplayText.builder(textList, isStructureFormed(), false) - .addLowPowerLine(!drainEnergy(true)) - .addCustom(tl -> { - if (isStructureFormed() && !isClean()) { - tl.add(TextComponentUtil.translationWithColor( - TextFormatting.YELLOW, + protected void configureWarningText(MultiblockUIBuilder builder) { + boolean lowPower = false; + if (isStructureFormed() && !getWorld().isRemote) { + lowPower = !drainEnergy(true); + } + builder.addLowPowerLine(lowPower) + .addCustom((list, syncer) -> { + if (isStructureFormed() && !syncer.syncBoolean(isClean())) { + list.add(KeyUtil.lang(TextFormatting.YELLOW, "gregtech.multiblock.cleanroom.warning_contaminated")); } - }) - .addMaintenanceProblemLines(getMaintenanceProblems()); + + if (!syncer.syncBoolean(cleanroomLogic.isVoltageHighEnough())) { + IKey energyNeeded = IKey.str(GTValues.VNF[cleanroomFilter.getMinTier()]); + list.add(KeyUtil.lang(TextFormatting.YELLOW, "gregtech.multiblock.cleanroom.low_tier", + energyNeeded)); + } + }); + super.configureWarningText(builder); } @Override @@ -780,7 +774,7 @@ public List getMatchingShapes() { } @Override - protected boolean shouldShowVoidingModeButton() { + public boolean shouldShowVoidingModeButton() { return false; } } 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 b1bd30b8574..a20c11176c1 100644 --- a/src/main/java/gregtech/common/metatileentities/multi/electric/MetaTileEntityCrackingUnit.java +++ b/src/main/java/gregtech/common/metatileentities/multi/electric/MetaTileEntityCrackingUnit.java @@ -5,8 +5,8 @@ import gregtech.api.metatileentity.MetaTileEntity; import gregtech.api.metatileentity.interfaces.IGregTechTileEntity; import gregtech.api.metatileentity.multiblock.IMultiblockPart; -import gregtech.api.metatileentity.multiblock.MultiblockDisplayText; import gregtech.api.metatileentity.multiblock.RecipeMapMultiblockController; +import gregtech.api.metatileentity.multiblock.ui.MultiblockUIBuilder; import gregtech.api.pattern.BlockPattern; import gregtech.api.pattern.FactoryBlockPattern; import gregtech.api.pattern.PatternMatchContext; @@ -14,7 +14,7 @@ import gregtech.api.recipes.logic.OCResult; import gregtech.api.recipes.properties.RecipePropertyStorage; import gregtech.api.util.GTUtility; -import gregtech.api.util.TextComponentUtil; +import gregtech.api.util.KeyUtil; import gregtech.client.renderer.ICubeRenderer; import gregtech.client.renderer.texture.Textures; import gregtech.common.blocks.BlockMetalCasing; @@ -26,12 +26,12 @@ import net.minecraft.item.ItemStack; import net.minecraft.util.ResourceLocation; import net.minecraft.util.SoundEvent; -import net.minecraft.util.text.ITextComponent; import net.minecraft.util.text.TextFormatting; import net.minecraft.world.World; import net.minecraftforge.fml.relauncher.Side; import net.minecraftforge.fml.relauncher.SideOnly; +import com.cleanroommc.modularui.api.drawable.IKey; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; @@ -80,32 +80,30 @@ public SoundEvent getBreakdownSound() { } @Override - protected void addDisplayText(List textList) { - MultiblockDisplayText.builder(textList, isStructureFormed()) - .setWorkingStatus(recipeMapWorkable.isWorkingEnabled(), recipeMapWorkable.isActive()) + protected void configureDisplayText(MultiblockUIBuilder builder) { + builder.setWorkingStatus(recipeMapWorkable.isWorkingEnabled(), recipeMapWorkable.isActive()) .addEnergyUsageLine(getEnergyContainer()) .addEnergyTierLine(GTUtility.getTierByVoltage(recipeMapWorkable.getMaxVoltage())) - .addCustom(tl -> { + .addCustom((textList, syncer) -> { + if (!isStructureFormed()) return; + // Coil energy discount line - if (isStructureFormed()) { - ITextComponent energyDiscount = TextComponentUtil.stringWithColor(TextFormatting.AQUA, - (100 - 10 * coilTier) + "%"); + IKey energyDiscount = KeyUtil.number(TextFormatting.AQUA, + syncer.syncLong(100 - 10L * getCoilTier()), "%"); - ITextComponent base = TextComponentUtil.translationWithColor( - TextFormatting.GRAY, - "gregtech.multiblock.cracking_unit.energy", - energyDiscount); + IKey base = KeyUtil.lang(TextFormatting.GRAY, + "gregtech.multiblock.cracking_unit.energy", + energyDiscount); - ITextComponent hover = TextComponentUtil.translationWithColor( - TextFormatting.GRAY, - "gregtech.multiblock.cracking_unit.energy_hover"); + IKey hover = KeyUtil.lang(TextFormatting.GRAY, + "gregtech.multiblock.cracking_unit.energy_hover"); - tl.add(TextComponentUtil.setHover(base, hover)); - } + textList.add(KeyUtil.setHover(base, hover)); }) .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/MetaTileEntityDataBank.java b/src/main/java/gregtech/common/metatileentities/multi/electric/MetaTileEntityDataBank.java index 5b52829f001..9f8f7c9428b 100644 --- a/src/main/java/gregtech/common/metatileentities/multi/electric/MetaTileEntityDataBank.java +++ b/src/main/java/gregtech/common/metatileentities/multi/electric/MetaTileEntityDataBank.java @@ -7,8 +7,8 @@ import gregtech.api.metatileentity.interfaces.IGregTechTileEntity; import gregtech.api.metatileentity.multiblock.IMultiblockPart; import gregtech.api.metatileentity.multiblock.MultiblockAbility; -import gregtech.api.metatileentity.multiblock.MultiblockDisplayText; import gregtech.api.metatileentity.multiblock.MultiblockWithDisplayBase; +import gregtech.api.metatileentity.multiblock.ui.MultiblockUIBuilder; import gregtech.api.pattern.BlockPattern; import gregtech.api.pattern.FactoryBlockPattern; import gregtech.api.pattern.PatternMatchContext; @@ -28,7 +28,6 @@ import net.minecraft.util.EnumFacing; import net.minecraft.util.ResourceLocation; import net.minecraft.util.SoundEvent; -import net.minecraft.util.text.ITextComponent; import net.minecraft.world.World; import net.minecraftforge.common.capabilities.Capability; import net.minecraftforge.fml.relauncher.Side; @@ -226,7 +225,7 @@ protected ICubeRenderer getFrontOverlay() { } @Override - protected boolean shouldShowVoidingModeButton() { + public boolean shouldShowVoidingModeButton() { return false; } @@ -250,11 +249,9 @@ public void addInformation(ItemStack stack, @Nullable World world, @NotNull List } @Override - protected void addDisplayText(List textList) { - MultiblockDisplayText.builder(textList, isStructureFormed()) - .setWorkingStatus(true, isActive() && isWorkingEnabled()) // transform into two-state system for display - .setWorkingStatusKeys( - "gregtech.multiblock.idling", + protected void configureDisplayText(MultiblockUIBuilder builder) { + builder.setWorkingStatus(true, isActive() && isWorkingEnabled()) // transform into two-state system for display + .setWorkingStatusKeys("gregtech.multiblock.idling", "gregtech.multiblock.idling", "gregtech.multiblock.data_bank.providing") .addEnergyUsageExactLine(getEnergyUsage()) @@ -262,10 +259,9 @@ protected void addDisplayText(List textList) { } @Override - protected void addWarningText(List textList) { - MultiblockDisplayText.builder(textList, isStructureFormed(), false) - .addLowPowerLine(hasNotEnoughEnergy) - .addMaintenanceProblemLines(getMaintenanceProblems()); + protected void configureWarningText(MultiblockUIBuilder builder) { + builder.addLowPowerLine(hasNotEnoughEnergy); + super.configureWarningText(builder); } @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 89c277d7c8d..b70680943f2 100644 --- a/src/main/java/gregtech/common/metatileentities/multi/electric/MetaTileEntityElectricBlastFurnace.java +++ b/src/main/java/gregtech/common/metatileentities/multi/electric/MetaTileEntityElectricBlastFurnace.java @@ -9,8 +9,10 @@ import gregtech.api.metatileentity.interfaces.IGregTechTileEntity; import gregtech.api.metatileentity.multiblock.IMultiblockPart; import gregtech.api.metatileentity.multiblock.MultiblockAbility; -import gregtech.api.metatileentity.multiblock.MultiblockDisplayText; import gregtech.api.metatileentity.multiblock.RecipeMapMultiblockController; +import gregtech.api.metatileentity.multiblock.ui.KeyManager; +import gregtech.api.metatileentity.multiblock.ui.MultiblockUIBuilder; +import gregtech.api.metatileentity.multiblock.ui.UISyncer; import gregtech.api.pattern.BlockPattern; import gregtech.api.pattern.FactoryBlockPattern; import gregtech.api.pattern.MultiblockShapeInfo; @@ -19,7 +21,7 @@ import gregtech.api.recipes.RecipeMaps; import gregtech.api.recipes.properties.impl.TemperatureProperty; import gregtech.api.util.GTUtility; -import gregtech.api.util.TextComponentUtil; +import gregtech.api.util.KeyUtil; import gregtech.api.util.TextFormattingUtil; import gregtech.client.renderer.ICubeRenderer; import gregtech.client.renderer.texture.Textures; @@ -69,38 +71,32 @@ public MetaTileEntity createMetaTileEntity(IGregTechTileEntity tileEntity) { } @Override - protected void addDisplayText(List textList) { - MultiblockDisplayText.builder(textList, isStructureFormed()) - .setWorkingStatus(recipeMapWorkable.isWorkingEnabled(), recipeMapWorkable.isActive()) - .addEnergyUsageLine(getEnergyContainer()) + protected void configureDisplayText(MultiblockUIBuilder builder) { + builder.setWorkingStatus(recipeMapWorkable.isWorkingEnabled(), recipeMapWorkable.isActive()) + .addEnergyUsageLine(this.getEnergyContainer()) .addEnergyTierLine(GTUtility.getTierByVoltage(recipeMapWorkable.getMaxVoltage())) - .addCustom(tl -> { - // Coil heat capacity line - if (isStructureFormed()) { - ITextComponent heatString = TextComponentUtil.stringWithColor( - TextFormatting.RED, - TextFormattingUtil.formatNumbers(blastFurnaceTemperature) + "K"); - - tl.add(TextComponentUtil.translationWithColor( - TextFormatting.GRAY, - "gregtech.multiblock.blast_furnace.max_temperature", - heatString)); - } - }) + .addCustom(this::addHeatCapacity) .addParallelsLine(recipeMapWorkable.getParallelLimit()) .addWorkingStatusLine() - .addProgressLine(recipeMapWorkable.getProgressPercent()); + .addProgressLine(recipeMapWorkable.getProgress(), recipeMapWorkable.getMaxProgress()) + .addRecipeOutputLine(recipeMapWorkable); + } + + private void addHeatCapacity(KeyManager keyManager, UISyncer syncer) { + if (isStructureFormed()) { + var heatString = KeyUtil.number(TextFormatting.RED, + syncer.syncInt(getCurrentTemperature()), "K"); + + keyManager.add(KeyUtil.lang(TextFormatting.GRAY, + "gregtech.multiblock.blast_furnace.max_temperature", heatString)); + } } @Override protected void formStructure(PatternMatchContext context) { super.formStructure(context); - Object type = context.get("CoilType"); - if (type instanceof IHeatingCoilBlockStats) { - this.blastFurnaceTemperature = ((IHeatingCoilBlockStats) type).getCoilTemperature(); - } else { - this.blastFurnaceTemperature = CoilType.CUPRONICKEL.getCoilTemperature(); - } + IHeatingCoilBlockStats type = context.getOrDefault("CoilType", CoilType.CUPRONICKEL); + this.blastFurnaceTemperature = type.getCoilTemperature(); // the subtracted tier gives the starting level (exclusive) of the +100K heat bonus this.blastFurnaceTemperature += 100 * Math.max(0, GTUtility.getFloorTierByVoltage(getEnergyContainer().getInputVoltage()) - GTValues.MV); 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 dca07a39e5f..19c09a0def4 100644 --- a/src/main/java/gregtech/common/metatileentities/multi/electric/MetaTileEntityFluidDrill.java +++ b/src/main/java/gregtech/common/metatileentities/multi/electric/MetaTileEntityFluidDrill.java @@ -8,12 +8,13 @@ import gregtech.api.capability.impl.EnergyContainerList; import gregtech.api.capability.impl.FluidDrillLogic; import gregtech.api.capability.impl.FluidTankList; -import gregtech.api.gui.GuiTextures; -import gregtech.api.gui.resources.TextureArea; import gregtech.api.metatileentity.ITieredMetaTileEntity; import gregtech.api.metatileentity.MetaTileEntity; import gregtech.api.metatileentity.interfaces.IGregTechTileEntity; import gregtech.api.metatileentity.multiblock.*; +import gregtech.api.metatileentity.multiblock.ui.MultiblockUIBuilder; +import gregtech.api.metatileentity.multiblock.ui.TemplateBarBuilder; +import gregtech.api.mui.GTGuiTextures; import gregtech.api.pattern.BlockPattern; import gregtech.api.pattern.FactoryBlockPattern; import gregtech.api.pattern.PatternMatchContext; @@ -21,7 +22,7 @@ import gregtech.api.unification.material.Materials; import gregtech.api.util.GTTransferUtils; import gregtech.api.util.GTUtility; -import gregtech.api.util.TextComponentUtil; +import gregtech.api.util.KeyUtil; import gregtech.api.util.TextFormattingUtil; import gregtech.api.worldgen.bedrockFluids.BedrockFluidVeinHandler; import gregtech.client.renderer.ICubeRenderer; @@ -36,7 +37,6 @@ import net.minecraft.network.PacketBuffer; import net.minecraft.util.EnumFacing; import net.minecraft.util.ResourceLocation; -import net.minecraft.util.text.ITextComponent; import net.minecraft.util.text.TextFormatting; import net.minecraft.world.World; import net.minecraftforge.common.capabilities.Capability; @@ -48,15 +48,19 @@ import codechicken.lib.render.CCRenderState; import codechicken.lib.render.pipeline.IVertexOperation; import codechicken.lib.vec.Matrix4; +import com.cleanroommc.modularui.api.drawable.IKey; +import com.cleanroommc.modularui.value.sync.IntSyncValue; +import com.cleanroommc.modularui.value.sync.PanelSyncManager; import com.google.common.collect.Lists; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import java.util.Collections; import java.util.List; +import java.util.function.UnaryOperator; public class MetaTileEntityFluidDrill extends MultiblockWithDisplayBase - implements ITieredMetaTileEntity, IWorkable, IProgressBarMultiblock { + implements ITieredMetaTileEntity, IWorkable, ProgressBarMultiblock { private final FluidDrillLogic minerLogic; private final int tier; @@ -110,7 +114,7 @@ protected void updateFormedValid() { } @Override - protected BlockPattern createStructurePattern() { + protected @NotNull BlockPattern createStructurePattern() { return FactoryBlockPattern.start() .aisle("XXX", "#F#", "#F#", "#F#", "###", "###", "###") .aisle("XXX", "FCF", "FCF", "FCF", "#F#", "#F#", "#F#") @@ -159,61 +163,56 @@ public ICubeRenderer getBaseTexture(IMultiblockPart sourcePart) { } @Override - protected void addDisplayText(List textList) { - MultiblockDisplayText.builder(textList, isStructureFormed()) - .setWorkingStatus(minerLogic.isWorkingEnabled(), minerLogic.isActive()) + protected void configureDisplayText(MultiblockUIBuilder builder) { + builder.setWorkingStatus(minerLogic.isWorkingEnabled(), minerLogic.isActive()) .setWorkingStatusKeys( "gregtech.multiblock.idling", "gregtech.multiblock.work_paused", "gregtech.multiblock.miner.drilling") .addEnergyUsageLine(energyContainer) - .addCustom(tl -> { - if (isStructureFormed()) { - if (minerLogic.getDrilledFluid() != null) { - // Fluid name - Fluid drilledFluid = minerLogic.getDrilledFluid(); - ITextComponent fluidInfo = TextComponentUtil - .setColor(GTUtility.getFluidTranslation(drilledFluid), TextFormatting.GREEN); - tl.add(TextComponentUtil.translationWithColor( - TextFormatting.GRAY, - "gregtech.multiblock.fluid_rig.drilled_fluid", - fluidInfo)); - - // Fluid amount - ITextComponent amountInfo = TextComponentUtil.stringWithColor( - TextFormatting.BLUE, - TextFormattingUtil.formatNumbers( - minerLogic.getFluidToProduce() * 20L / FluidDrillLogic.MAX_PROGRESS) + - " L/s"); - tl.add(TextComponentUtil.translationWithColor( - TextFormatting.GRAY, - "gregtech.multiblock.fluid_rig.fluid_amount", - amountInfo)); - } else { - ITextComponent noFluid = TextComponentUtil.translationWithColor(TextFormatting.RED, - "gregtech.multiblock.fluid_rig.no_fluid_in_area"); - tl.add(TextComponentUtil.translationWithColor( - TextFormatting.GRAY, - "gregtech.multiblock.fluid_rig.drilled_fluid", - noFluid)); - } + .addCustom((keyManager, syncer) -> { + if (!isStructureFormed()) return; + + // Fluid name + Fluid drilledFluid = syncer.syncFluid(minerLogic.getDrilledFluid()); + if (drilledFluid == null) { + IKey noFluid = KeyUtil.lang(TextFormatting.RED, + "gregtech.multiblock.fluid_rig.no_fluid_in_area"); + + keyManager.add(KeyUtil.lang(TextFormatting.GRAY, + "gregtech.multiblock.fluid_rig.drilled_fluid", + noFluid)); + return; } + + IKey fluidInfo = KeyUtil.fluid(drilledFluid).style(TextFormatting.GREEN); + + keyManager.add(KeyUtil.lang(TextFormatting.GRAY, + "gregtech.multiblock.fluid_rig.drilled_fluid", + fluidInfo)); + + int fluidProduce = syncer.syncInt(minerLogic.getFluidToProduce()); + + IKey amountInfo = KeyUtil.number(TextFormatting.BLUE, + fluidProduce * 20L / FluidDrillLogic.MAX_PROGRESS, " L/s"); + + keyManager.add(KeyUtil.lang(TextFormatting.GRAY, + "gregtech.multiblock.fluid_rig.fluid_amount", + amountInfo)); }) - .addWorkingStatusLine() - .addProgressLine(minerLogic.getProgressPercent()); + .addProgressLine(minerLogic.getProgressTime(), FluidDrillLogic.MAX_PROGRESS) + .addWorkingStatusLine(); } @Override - protected void addWarningText(List textList) { - MultiblockDisplayText.builder(textList, isStructureFormed(), false) - .addLowPowerLine(isStructureFormed() && !drainEnergy(true)) - .addCustom(tl -> { - if (isStructureFormed() && minerLogic.isInventoryFull()) { - tl.add(TextComponentUtil.translationWithColor( - TextFormatting.YELLOW, - "gregtech.machine.miner.invfull")); + protected void configureWarningText(MultiblockUIBuilder builder) { + builder.addLowPowerLine(() -> isStructureFormed() && !drainEnergy(true)) + .addCustom((list, syncer) -> { + if (isStructureFormed() && syncer.syncBoolean(minerLogic.isInventoryFull())) { + list.add(KeyUtil.lang(TextFormatting.YELLOW, "gregtech.machine.miner.invfull")); } }); + super.configureWarningText(builder); } @Override @@ -367,50 +366,56 @@ public T getCapability(Capability capability, EnumFacing side) { } @Override - protected boolean shouldShowVoidingModeButton() { + public boolean shouldShowVoidingModeButton() { return false; } - @Override - public boolean showProgressBar() { - return tier > GTValues.MV; // only show for T2/3 fluid rigs + public boolean allowsExtendedFacing() { + return false; } @Override - public double getFillPercentage(int index) { - int numOperationsLeft = BedrockFluidVeinHandler.getOperationsRemaining(getWorld(), minerLogic.getChunkX(), - minerLogic.getChunkZ()); - int maxOperations = BedrockFluidVeinHandler.MAXIMUM_VEIN_OPERATIONS; - return 1.0 * numOperationsLeft / maxOperations; + public int getProgressBarCount() { + // only show for T2/3 fluid rigs + return tier > GTValues.MV ? 1 : 0; } @Override - public TextureArea getProgressBarTexture(int index) { - return GuiTextures.PROGRESS_BAR_FLUID_RIG_DEPLETION; + public void registerBars(List> bars, PanelSyncManager syncManager) { + IntSyncValue operationsValue = new IntSyncValue(() -> BedrockFluidVeinHandler.getOperationsRemaining(getWorld(), + minerLogic.getChunkX(), minerLogic.getChunkZ())); + syncManager.syncValue("operations_remaining", operationsValue); + + bars.add(bar -> bar + .progress(() -> operationsValue.getIntValue() * 1.0 / BedrockFluidVeinHandler.MAXIMUM_VEIN_OPERATIONS) + .texture(GTGuiTextures.PROGRESS_BAR_FLUID_RIG_DEPLETION) + .tooltipBuilder(t -> { + if (!isStructureFormed()) { + t.addLine(IKey.lang("gregtech.multiblock.invalid_structure")); + return; + } + + if (operationsValue.getIntValue() == 0) { + t.addLine(IKey.lang("gregtech.multiblock.fluid_rig.vein_depleted")); + return; + } + + t.addLine(KeyUtil.string(() -> getDepletionLang(operationsValue))); + })); } - @Override - public void addBarHoverText(List hoverList, int index) { - int numOperationsLeft = BedrockFluidVeinHandler.getOperationsRemaining(getWorld(), minerLogic.getChunkX(), - minerLogic.getChunkZ()); - int maxOperations = BedrockFluidVeinHandler.MAXIMUM_VEIN_OPERATIONS; - int percentage = (int) Math.round(1.0 * numOperationsLeft / maxOperations * 100); - TextFormatting color = percentage > 40 ? TextFormatting.GREEN : - percentage > 10 ? TextFormatting.YELLOW : TextFormatting.RED; - - if (numOperationsLeft == 0) { - hoverList.add(TextComponentUtil.translationWithColor(TextFormatting.RED, - "gregtech.multiblock.fluid_rig.vein_depleted")); + private static @NotNull String getDepletionLang(IntSyncValue operationsValue) { + int percent = (int) Math.round(100.0 * operationsValue.getIntValue() / + BedrockFluidVeinHandler.MAXIMUM_VEIN_OPERATIONS); + if (percent > 40) { + return TextFormatting.GREEN + IKey + .lang("gregtech.multiblock.fluid_rig.vein_depletion.high", percent).get(); + } else if (percent > 10) { + return TextFormatting.YELLOW + IKey + .lang("gregtech.multiblock.fluid_rig.vein_depletion.medium", percent).get(); } else { - ITextComponent veinInfo = TextComponentUtil.stringWithColor(color, percentage + "%"); - hoverList.add(TextComponentUtil.translationWithColor( - TextFormatting.GRAY, - "gregtech.multiblock.fluid_rig.vein_depletion", - veinInfo)); + return TextFormatting.RED + IKey + .lang("gregtech.multiblock.fluid_rig.vein_depletion.low", percent).get(); } } - - public boolean allowsExtendedFacing() { - return false; - } } diff --git a/src/main/java/gregtech/common/metatileentities/multi/electric/MetaTileEntityFusionReactor.java b/src/main/java/gregtech/common/metatileentities/multi/electric/MetaTileEntityFusionReactor.java index f9db6f997d9..eb63793d711 100644 --- a/src/main/java/gregtech/common/metatileentities/multi/electric/MetaTileEntityFusionReactor.java +++ b/src/main/java/gregtech/common/metatileentities/multi/electric/MetaTileEntityFusionReactor.java @@ -6,21 +6,16 @@ import gregtech.api.capability.impl.EnergyContainerHandler; import gregtech.api.capability.impl.EnergyContainerList; import gregtech.api.capability.impl.MultiblockRecipeLogic; -import gregtech.api.gui.GuiTextures; -import gregtech.api.gui.ModularUI; -import gregtech.api.gui.resources.TextureArea; -import gregtech.api.gui.widgets.ImageCycleButtonWidget; -import gregtech.api.gui.widgets.ImageWidget; -import gregtech.api.gui.widgets.IndicatorImageWidget; -import gregtech.api.gui.widgets.ProgressWidget; import gregtech.api.metatileentity.IFastRenderMetaTileEntity; import gregtech.api.metatileentity.MetaTileEntity; import gregtech.api.metatileentity.interfaces.IGregTechTileEntity; import gregtech.api.metatileentity.multiblock.IMultiblockPart; import gregtech.api.metatileentity.multiblock.MultiblockAbility; -import gregtech.api.metatileentity.multiblock.MultiblockDisplayText; -import gregtech.api.metatileentity.multiblock.MultiblockWithDisplayBase; +import gregtech.api.metatileentity.multiblock.ProgressBarMultiblock; import gregtech.api.metatileentity.multiblock.RecipeMapMultiblockController; +import gregtech.api.metatileentity.multiblock.ui.MultiblockUIFactory; +import gregtech.api.metatileentity.multiblock.ui.TemplateBarBuilder; +import gregtech.api.mui.GTGuiTextures; import gregtech.api.pattern.BlockPattern; import gregtech.api.pattern.FactoryBlockPattern; import gregtech.api.pattern.MultiblockShapeInfo; @@ -30,9 +25,8 @@ import gregtech.api.recipes.logic.OCParams; import gregtech.api.recipes.properties.RecipePropertyStorage; import gregtech.api.recipes.properties.impl.FusionEUToStartProperty; +import gregtech.api.util.KeyUtil; import gregtech.api.util.RelativeDirection; -import gregtech.api.util.TextComponentUtil; -import gregtech.api.util.TextFormattingUtil; import gregtech.api.util.interpolate.Eases; import gregtech.client.renderer.ICubeRenderer; import gregtech.client.renderer.IRenderSetup; @@ -57,7 +51,6 @@ import net.minecraft.client.renderer.Tessellator; import net.minecraft.client.renderer.vertex.DefaultVertexFormats; import net.minecraft.client.resources.I18n; -import net.minecraft.entity.player.EntityPlayer; import net.minecraft.init.Blocks; import net.minecraft.item.ItemStack; import net.minecraft.nbt.NBTTagCompound; @@ -65,14 +58,19 @@ import net.minecraft.util.EnumFacing; import net.minecraft.util.ResourceLocation; import net.minecraft.util.math.AxisAlignedBB; -import net.minecraft.util.text.ITextComponent; import net.minecraft.util.text.TextFormatting; import net.minecraft.world.World; import net.minecraftforge.fml.relauncher.Side; import net.minecraftforge.fml.relauncher.SideOnly; +import com.cleanroommc.modularui.api.drawable.IDrawable; +import com.cleanroommc.modularui.api.drawable.IKey; +import com.cleanroommc.modularui.value.sync.DoubleSyncValue; +import com.cleanroommc.modularui.value.sync.LongSyncValue; +import com.cleanroommc.modularui.value.sync.PanelSyncManager; +import com.cleanroommc.modularui.widgets.ProgressWidget; +import com.cleanroommc.modularui.widgets.layout.Column; import com.google.common.collect.Lists; -import com.google.common.util.concurrent.AtomicDouble; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import org.lwjgl.opengl.GL11; @@ -80,14 +78,14 @@ import java.util.ArrayList; import java.util.Arrays; import java.util.List; -import java.util.function.DoubleSupplier; +import java.util.function.UnaryOperator; import static gregtech.api.recipes.logic.OverclockingLogic.PERFECT_HALF_DURATION_FACTOR; import static gregtech.api.recipes.logic.OverclockingLogic.PERFECT_HALF_VOLTAGE_FACTOR; import static gregtech.api.util.RelativeDirection.*; public class MetaTileEntityFusionReactor extends RecipeMapMultiblockController - implements IFastRenderMetaTileEntity, IBloomEffect { + implements IFastRenderMetaTileEntity, IBloomEffect, ProgressBarMultiblock { protected static final int NO_COLOR = 0; @@ -95,7 +93,6 @@ public class MetaTileEntityFusionReactor extends RecipeMapMultiblockController private EnergyContainerList inputEnergyContainers; private long heat = 0; // defined in TileEntityFusionReactor but serialized in FusionRecipeLogic private int fusionRingColor = NO_COLOR; - private final FusionProgressSupplier progressBarSupplier; @SideOnly(Side.CLIENT) private boolean registeredBloomRenderTicket; @@ -112,7 +109,6 @@ public String getName() { return GregtechDataCodes.FUSION_REACTOR_ENERGY_CONTAINER_TRAIT; } }; - this.progressBarSupplier = new FusionProgressSupplier(); } @Override @@ -355,219 +351,86 @@ public long getHeat() { } @Override - protected ModularUI.Builder createUITemplate(EntityPlayer entityPlayer) { - // Background - ModularUI.Builder builder = ModularUI.builder(GuiTextures.BACKGROUND, 198, 236); - - // Display - builder.image(4, 4, 190, 138, GuiTextures.DISPLAY); - - // Energy Bar - builder.widget(new ProgressWidget( - () -> energyContainer.getEnergyCapacity() > 0 ? - 1.0 * energyContainer.getEnergyStored() / energyContainer.getEnergyCapacity() : 0, - 4, 144, 94, 7, - GuiTextures.PROGRESS_BAR_FUSION_ENERGY, ProgressWidget.MoveType.HORIZONTAL) - .setHoverTextConsumer(this::addEnergyBarHoverText)); - - // Heat Bar - builder.widget(new ProgressWidget( - () -> energyContainer.getEnergyCapacity() > 0 ? 1.0 * heat / energyContainer.getEnergyCapacity() : 0, - 100, 144, 94, 7, - GuiTextures.PROGRESS_BAR_FUSION_HEAT, ProgressWidget.MoveType.HORIZONTAL) - .setHoverTextConsumer(this::addHeatBarHoverText)); - - // Indicator Widget - builder.widget(new IndicatorImageWidget(174, 122, 17, 17, getLogo()) - .setWarningStatus(getWarningLogo(), this::addWarningText) - .setErrorStatus(getErrorLogo(), this::addErrorText)); - - // Title + protected MultiblockUIFactory createUIFactory() { + IDrawable title; if (tier == GTValues.LuV) { // MK1 - builder.widget(new ImageWidget(66, 9, 67, 12, GuiTextures.FUSION_REACTOR_MK1_TITLE).setIgnoreColor(true)); + title = GTGuiTextures.FUSION_REACTOR_MK1_TITLE; } else if (tier == GTValues.ZPM) { // MK2 - builder.widget(new ImageWidget(65, 9, 69, 12, GuiTextures.FUSION_REACTOR_MK2_TITLE).setIgnoreColor(true)); + title = GTGuiTextures.FUSION_REACTOR_MK2_TITLE; } else { // MK3 - builder.widget(new ImageWidget(64, 9, 71, 12, GuiTextures.FUSION_REACTOR_MK3_TITLE).setIgnoreColor(true)); + title = GTGuiTextures.FUSION_REACTOR_MK3_TITLE; } - // Fusion Diagram + Progress Bar - builder.widget(new ImageWidget(55, 24, 89, 101, GuiTextures.FUSION_REACTOR_DIAGRAM).setIgnoreColor(true)); - builder.widget(FusionProgressSupplier.Type.BOTTOM_LEFT.getWidget(this)); - builder.widget(FusionProgressSupplier.Type.TOP_LEFT.getWidget(this)); - builder.widget(FusionProgressSupplier.Type.TOP_RIGHT.getWidget(this)); - builder.widget(FusionProgressSupplier.Type.BOTTOM_RIGHT.getWidget(this)); - - // Fusion Legend - builder.widget(new ImageWidget(7, 98, 108, 41, GuiTextures.FUSION_REACTOR_LEGEND).setIgnoreColor(true)); - - // Power Button + Detail - builder.widget(new ImageCycleButtonWidget(173, 211, 18, 18, GuiTextures.BUTTON_POWER, - recipeMapWorkable::isWorkingEnabled, recipeMapWorkable::setWorkingEnabled)); - builder.widget(new ImageWidget(173, 229, 18, 6, GuiTextures.BUTTON_POWER_DETAIL)); - - // Voiding Mode Button - builder.widget(new ImageCycleButtonWidget(173, 189, 18, 18, GuiTextures.BUTTON_VOID_MULTIBLOCK, - 4, this::getVoidingMode, this::setVoidingMode) - .setTooltipHoverString(MultiblockWithDisplayBase::getVoidingModeTooltip)); - - // Distinct Buses Unavailable Image - builder.widget(new ImageWidget(173, 171, 18, 18, GuiTextures.BUTTON_NO_DISTINCT_BUSES) - .setTooltip("gregtech.multiblock.universal.distinct_not_supported")); - - // Flex Unavailable Image - builder.widget(getFlexButton(173, 153, 18, 18)); - - // Player Inventory - builder.bindPlayerInventory(entityPlayer.inventory, 153); - return builder; - } - - private void addEnergyBarHoverText(List hoverList) { - ITextComponent energyInfo = TextComponentUtil.stringWithColor( - TextFormatting.AQUA, - TextFormattingUtil.formatNumbers(energyContainer.getEnergyStored()) + " / " + - TextFormattingUtil.formatNumbers(energyContainer.getEnergyCapacity()) + " EU"); - hoverList.add(TextComponentUtil.translationWithColor( - TextFormatting.GRAY, - "gregtech.multiblock.energy_stored", - energyInfo)); - } - - private void addHeatBarHoverText(List hoverList) { - ITextComponent heatInfo = TextComponentUtil.stringWithColor( - TextFormatting.RED, - TextFormattingUtil.formatNumbers(heat) + " / " + - TextFormattingUtil.formatNumbers(energyContainer.getEnergyCapacity())); - hoverList.add(TextComponentUtil.translationWithColor( - TextFormatting.GRAY, - "gregtech.multiblock.fusion_reactor.heat", - heatInfo)); - } - - private static class FusionProgressSupplier { - - private final AtomicDouble tracker = new AtomicDouble(0.0); - private final ProgressWidget.TimedProgressSupplier bottomLeft; - private final DoubleSupplier topLeft; - private final DoubleSupplier topRight; - private final DoubleSupplier bottomRight; - - public FusionProgressSupplier() { - // Bottom Left, fill on [0, 0.25) - bottomLeft = new ProgressWidget.TimedProgressSupplier(200, 164, false) { - - @Override - public double getAsDouble() { - double val = super.getAsDouble(); - tracker.set(val); - if (val >= 0.25) { - return 1; - } - return 4 * val; - } - - @Override - public void resetCountdown() { - super.resetCountdown(); - tracker.set(0); - } - }; - - // Top Left, fill on [0.25, 0.5) - topLeft = () -> { - double val = tracker.get(); - if (val < 0.25) { - return 0; - } else if (val >= 0.5) { - return 1; - } - return 4 * (val - 0.25); - }; - - // Top Right, fill on [0.5, 0.75) - topRight = () -> { - double val = tracker.get(); - if (val < 0.5) { - return 0; - } else if (val >= 0.75) { - return 1; - } - return 4 * (val - 0.5); - }; - - // Bottom Right, fill on [0.75, 1.0] - bottomRight = () -> { - double val = tracker.get(); - if (val < 0.75) { - return 0; - } else if (val >= 1) { - return 1; - } - return 4 * (val - 0.75); - }; - } - - public void resetCountdown() { - bottomLeft.resetCountdown(); - } - - public DoubleSupplier getSupplier(Type type) { - return switch (type) { - case BOTTOM_LEFT -> bottomLeft; - case TOP_LEFT -> topLeft; - case TOP_RIGHT -> topRight; - case BOTTOM_RIGHT -> bottomRight; - }; - } + DoubleSyncValue progress = new DoubleSyncValue(recipeMapWorkable::getProgressPercent); + return new MultiblockUIFactory(this) + .setScreenHeight(138) + .disableDisplayText() + .addScreenChildren((parent, syncManager) -> { + var status = MultiblockUIFactory.builder("status", syncManager); + status.setAction(b -> b.structureFormed(true) + .setWorkingStatus(recipeMapWorkable.isWorkingEnabled(), recipeMapWorkable.isActive()) + .addWorkingStatusLine()); + parent.child(new Column() + .padding(4) + .expanded() + .child(title.asWidget() + .marginBottom(8) + .size(69, 12)) + .child(new ProgressWidget() + .size(77, 77) + .tooltipAutoUpdate(true) + .tooltipBuilder(status::build) + .background(GTGuiTextures.FUSION_DIAGRAM.asIcon() + .size(89, 101) + .marginTop(11)) + .direction(ProgressWidget.Direction.CIRCULAR_CW) + .value(progress) + .texture(null, GTGuiTextures.FUSION_PROGRESS, 77)) + .child(GTGuiTextures.FUSION_LEGEND.asWidget() + .left(4) + .bottom(4) + .size(108, 41))); + }); + } - private enum Type { - - BOTTOM_LEFT( - 61, 66, 35, 41, - GuiTextures.PROGRESS_BAR_FUSION_REACTOR_DIAGRAM_BL, ProgressWidget.MoveType.VERTICAL), - TOP_LEFT( - 61, 30, 41, 35, - GuiTextures.PROGRESS_BAR_FUSION_REACTOR_DIAGRAM_TL, ProgressWidget.MoveType.HORIZONTAL), - TOP_RIGHT( - 103, 30, 35, 41, - GuiTextures.PROGRESS_BAR_FUSION_REACTOR_DIAGRAM_TR, ProgressWidget.MoveType.VERTICAL_DOWNWARDS), - BOTTOM_RIGHT( - 97, 72, 41, 35, - GuiTextures.PROGRESS_BAR_FUSION_REACTOR_DIAGRAM_BR, ProgressWidget.MoveType.HORIZONTAL_BACKWARDS); - - private final int x; - private final int y; - private final int width; - private final int height; - private final TextureArea texture; - private final ProgressWidget.MoveType moveType; - - Type(int x, int y, int width, int height, TextureArea texture, ProgressWidget.MoveType moveType) { - this.x = x; - this.y = y; - this.width = width; - this.height = height; - this.texture = texture; - this.moveType = moveType; - } + @Override + public int getProgressBarCount() { + return 2; + } - public ProgressWidget getWidget(MetaTileEntityFusionReactor instance) { - return new ProgressWidget( - () -> instance.recipeMapWorkable.isActive() ? - instance.progressBarSupplier.getSupplier(this).getAsDouble() : 0, - x, y, width, height, texture, moveType) - .setIgnoreColor(true) - .setHoverTextConsumer( - tl -> MultiblockDisplayText.builder(tl, instance.isStructureFormed()) - .setWorkingStatus(instance.recipeMapWorkable.isWorkingEnabled(), - instance.recipeMapWorkable.isActive()) - .addWorkingStatusLine()); - } - } + @Override + public void registerBars(List> bars, PanelSyncManager syncManager) { + LongSyncValue capacity = new LongSyncValue(energyContainer::getEnergyCapacity); + syncManager.syncValue("capacity", capacity); + LongSyncValue stored = new LongSyncValue(energyContainer::getEnergyStored); + syncManager.syncValue("stored", stored); + LongSyncValue heat = new LongSyncValue(this::getHeat); + syncManager.syncValue("heat", heat); + + bars.add(barTest -> barTest + .progress(() -> capacity.getLongValue() > 0 ? + 1.0 * stored.getLongValue() / capacity.getLongValue() : 0) + .texture(GTGuiTextures.PROGRESS_BAR_FUSION_ENERGY) + .tooltipBuilder(tooltip -> tooltip + .add(KeyUtil.lang(TextFormatting.GRAY, + "gregtech.multiblock.energy_stored", + stored.getLongValue(), capacity.getLongValue())))); + + bars.add(barTest -> barTest + .texture(GTGuiTextures.PROGRESS_BAR_FUSION_HEAT) + .tooltipBuilder(tooltip -> { + IKey heatInfo = KeyUtil.string(TextFormatting.AQUA, + "%,d / %,d EU", + heat.getLongValue(), capacity.getLongValue()); + tooltip.add(KeyUtil.lang(TextFormatting.GRAY, + "gregtech.multiblock.fusion_reactor.heat", + heatInfo)); + }) + .progress(() -> capacity.getLongValue() > 0 ? + 1.0 * heat.getLongValue() / capacity.getLongValue() : 0)); } private class FusionRecipeLogic extends MultiblockRecipeLogic { @@ -657,14 +520,6 @@ public void deserializeNBT(@NotNull NBTTagCompound compound) { super.deserializeNBT(compound); heat = compound.getLong("Heat"); } - - @Override - protected void setActive(boolean active) { - if (active != isActive) { - MetaTileEntityFusionReactor.this.progressBarSupplier.resetCountdown(); - } - super.setActive(active); - } } @Override diff --git a/src/main/java/gregtech/common/metatileentities/multi/electric/MetaTileEntityHPCA.java b/src/main/java/gregtech/common/metatileentities/multi/electric/MetaTileEntityHPCA.java index f1bf2dc9593..80ba3026917 100644 --- a/src/main/java/gregtech/common/metatileentities/multi/electric/MetaTileEntityHPCA.java +++ b/src/main/java/gregtech/common/metatileentities/multi/electric/MetaTileEntityHPCA.java @@ -4,24 +4,24 @@ import gregtech.api.capability.*; import gregtech.api.capability.impl.EnergyContainerList; import gregtech.api.capability.impl.FluidTankList; -import gregtech.api.gui.GuiTextures; -import gregtech.api.gui.ModularUI; -import gregtech.api.gui.resources.IGuiTexture; -import gregtech.api.gui.resources.TextureArea; -import gregtech.api.gui.widgets.ProgressWidget; -import gregtech.api.gui.widgets.SuppliedImageWidget; import gregtech.api.metatileentity.MetaTileEntity; import gregtech.api.metatileentity.interfaces.IGregTechTileEntity; import gregtech.api.metatileentity.multiblock.*; +import gregtech.api.metatileentity.multiblock.ui.KeyManager; +import gregtech.api.metatileentity.multiblock.ui.MultiblockUIBuilder; +import gregtech.api.metatileentity.multiblock.ui.MultiblockUIFactory; +import gregtech.api.metatileentity.multiblock.ui.TemplateBarBuilder; +import gregtech.api.metatileentity.multiblock.ui.UISyncer; +import gregtech.api.mui.GTGuiTextures; import gregtech.api.pattern.BlockPattern; import gregtech.api.pattern.FactoryBlockPattern; import gregtech.api.pattern.MultiblockShapeInfo; import gregtech.api.pattern.PatternMatchContext; import gregtech.api.unification.material.Materials; import gregtech.api.util.GTUtility; +import gregtech.api.util.KeyUtil; import gregtech.api.util.RelativeDirection; -import gregtech.api.util.TextComponentUtil; -import gregtech.api.util.TextFormattingUtil; +import gregtech.api.util.function.impl.TimedProgressSupplier; import gregtech.client.renderer.ICubeRenderer; import gregtech.client.renderer.texture.Textures; import gregtech.common.ConfigHolder; @@ -32,7 +32,6 @@ import net.minecraft.block.state.IBlockState; import net.minecraft.client.resources.I18n; -import net.minecraft.entity.player.EntityPlayer; import net.minecraft.item.ItemStack; import net.minecraft.nbt.NBTTagCompound; import net.minecraft.network.PacketBuffer; @@ -53,6 +52,16 @@ import codechicken.lib.render.CCRenderState; import codechicken.lib.render.pipeline.IVertexOperation; import codechicken.lib.vec.Matrix4; +import com.cleanroommc.modularui.api.drawable.IKey; +import com.cleanroommc.modularui.drawable.DynamicDrawable; +import com.cleanroommc.modularui.drawable.UITexture; +import com.cleanroommc.modularui.value.sync.DoubleSyncValue; +import com.cleanroommc.modularui.value.sync.IntSyncValue; +import com.cleanroommc.modularui.value.sync.PanelSyncManager; +import com.cleanroommc.modularui.widget.ParentWidget; +import com.cleanroommc.modularui.widget.Widget; +import com.cleanroommc.modularui.widgets.ProgressWidget; +import com.cleanroommc.modularui.widgets.layout.Grid; import it.unimi.dsi.fastutil.objects.ObjectArrayList; import it.unimi.dsi.fastutil.objects.ObjectOpenHashSet; import org.jetbrains.annotations.NotNull; @@ -62,12 +71,12 @@ import java.util.Collection; import java.util.List; import java.util.Set; -import java.util.function.Supplier; +import java.util.function.UnaryOperator; import static gregtech.api.util.RelativeDirection.*; public class MetaTileEntityHPCA extends MultiblockWithDisplayBase - implements IOpticalComputationProvider, IControllable, IProgressBarMultiblock { + implements IOpticalComputationProvider, IControllable, ProgressBarMultiblock { private static final double IDLE_TEMPERATURE = 200; private static final double DAMAGE_TEMPERATURE = 1000; @@ -82,12 +91,12 @@ public class MetaTileEntityHPCA extends MultiblockWithDisplayBase private double temperature = IDLE_TEMPERATURE; // start at idle temperature - private final ProgressWidget.TimedProgressSupplier progressSupplier; + private final TimedProgressSupplier progressSupplier; public MetaTileEntityHPCA(ResourceLocation metaTileEntityId) { super(metaTileEntityId); this.energyContainer = new EnergyContainerList(new ArrayList<>()); - this.progressSupplier = new ProgressWidget.TimedProgressSupplier(200, 47, false); + this.progressSupplier = new TimedProgressSupplier(200, 47, false); this.hpcaHandler = new HPCAGridHandler(this); } @@ -353,106 +362,102 @@ public void setWorkingEnabled(boolean isWorkingAllowed) { } @Override - protected ModularUI.Builder createUITemplate(EntityPlayer entityPlayer) { - ModularUI.Builder builder = super.createUITemplate(entityPlayer); - - // Create the hover grid - builder.widget(new ProgressWidget( - () -> hpcaHandler.getAllocatedCWUt() > 0 ? progressSupplier.getAsDouble() : 0, - 74, 57, 47, 47, GuiTextures.HPCA_COMPONENT_OUTLINE, ProgressWidget.MoveType.HORIZONTAL) - .setIgnoreColor(true) - .setHoverTextConsumer(hpcaHandler::addInfo)); - int startX = 76; - int startY = 59; - for (int i = 0; i < 3; i++) { - for (int j = 0; j < 3; j++) { - final int index = i * 3 + j; - Supplier textureSupplier = () -> hpcaHandler.getComponentTexture(index); - builder.widget(new SuppliedImageWidget(startX + (15 * j), startY + (15 * i), 13, 13, textureSupplier) - .setIgnoreColor(true)); - } - } - return builder; - } - - @Override - protected void addDisplayText(List textList) { - MultiblockDisplayText.builder(textList, isStructureFormed()) - .setWorkingStatus(true, hpcaHandler.getAllocatedCWUt() > 0) // transform into two-state system for - // display + protected MultiblockUIFactory createUIFactory() { + return super.createUIFactory() + .addScreenChildren((parent, syncManager) -> { + MultiblockUIBuilder builder = MultiblockUIFactory.builder("hpca_tooltip", syncManager); + builder.setAction(b -> b.addCustom(hpcaHandler::addInfo)); + + parent.child(new ParentWidget<>() + .leftRel(0.5f) + .bottom(5) + .size(16 * 3 + 2) + .child(new ProgressWidget() + .sizeRel(1f) + .value(new DoubleSyncValue(progressSupplier)) + .texture(GTGuiTextures.HPCA_COMPONENT_OUTLINE, 47) + .direction(ProgressWidget.Direction.LEFT) + .tooltipAutoUpdate(true)) + .child(new Grid() + .sizeRel(1f) + .padding(1) + .mapTo(3, 9, value -> new Widget<>() + .overlay(new DynamicDrawable(() -> hpcaHandler.getComponentTexture(value)) + .asIcon().size(14).marginLeft(2).marginTop(2)) + .tooltipAutoUpdate(true) + .tooltipBuilder(tooltip -> { + if (isStructureFormed()) { + tooltip.addLine(hpcaHandler.getComponentKey(value)); + tooltip.spaceLine(2); + } + builder.build(tooltip); + }) + .size(16) + .padding(1)))); + }); + } + + @Override + protected void configureDisplayText(MultiblockUIBuilder builder) { + builder.setWorkingStatus(true, hpcaHandler.getAllocatedCWUt() > 0) .setWorkingStatusKeys( "gregtech.multiblock.idling", "gregtech.multiblock.idling", "gregtech.multiblock.data_bank.providing") - .addCustom(tl -> { - if (isStructureFormed()) { - // Energy Usage - ITextComponent voltageName = new TextComponentString( - GTValues.VNF[GTUtility.getTierByVoltage(hpcaHandler.getMaxEUt())]); - tl.add(TextComponentUtil.translationWithColor( - TextFormatting.GRAY, - "gregtech.multiblock.hpca.energy", - TextFormattingUtil.formatNumbers(hpcaHandler.cachedEUt), - TextFormattingUtil.formatNumbers(hpcaHandler.getMaxEUt()), - voltageName)); - - // Provided Computation - ITextComponent cwutInfo = TextComponentUtil.stringWithColor( - TextFormatting.AQUA, - hpcaHandler.cachedCWUt + " / " + hpcaHandler.getMaxCWUt() + " CWU/t"); - tl.add(TextComponentUtil.translationWithColor( - TextFormatting.GRAY, - "gregtech.multiblock.hpca.computation", - cwutInfo)); - } + .addCustom((manager, syncer) -> { + if (!isStructureFormed()) return; + + // Energy Usage + String voltageName = syncer + .syncString(GTValues.VNF[GTUtility.getTierByVoltage(hpcaHandler.getMaxEUt())]); + manager.add(KeyUtil.lang(TextFormatting.GRAY, + "gregtech.multiblock.hpca.energy", + KeyUtil.number(syncer.syncLong(hpcaHandler.cachedEUt)), + KeyUtil.number(syncer.syncLong(hpcaHandler.getMaxEUt())), + IKey.str(voltageName))); + + // Provided Computation + manager.add(KeyUtil.lang("gregtech.multiblock.hpca.computation", + syncer.syncInt(hpcaHandler.cachedCWUt), + syncer.syncInt(hpcaHandler.getMaxCWUt()))); }) .addWorkingStatusLine(); } - private TextFormatting getDisplayTemperatureColor() { - if (temperature < 500) { - return TextFormatting.GREEN; - } else if (temperature < 750) { - return TextFormatting.YELLOW; - } - return TextFormatting.RED; - } - @Override - protected void addWarningText(List textList) { - MultiblockDisplayText.builder(textList, isStructureFormed(), false) - .addLowPowerLine(hasNotEnoughEnergy) - .addCustom(tl -> { - if (isStructureFormed()) { - if (temperature > 500) { - // Temperature warning - tl.add(TextComponentUtil.translationWithColor( - TextFormatting.YELLOW, - "gregtech.multiblock.hpca.warning_temperature")); - - // Active cooler overdrive warning - tl.add(TextComponentUtil.translationWithColor( - TextFormatting.GRAY, - "gregtech.multiblock.hpca.warning_temperature_active_cool")); - } + protected void configureWarningText(MultiblockUIBuilder builder) { + builder.addLowPowerLine(hasNotEnoughEnergy) + .addCustom((manager, syncer) -> { + if (!isStructureFormed()) return; - // Structure warnings - hpcaHandler.addWarnings(tl); + if (syncer.syncDouble(temperature) > 500) { + // Temperature warning + manager.add(KeyUtil.lang(TextFormatting.YELLOW, + "gregtech.multiblock.hpca.warning_temperature")); + + // Active cooler overdrive warning + manager.add(KeyUtil.lang(TextFormatting.GRAY, + "gregtech.multiblock.hpca.warning_temperature_active_cool")); } - }) - .addMaintenanceProblemLines(getMaintenanceProblems()); + + // Structure warnings + hpcaHandler.addWarnings(manager, syncer); + }); + super.configureWarningText(builder); } @Override - protected void addErrorText(List textList) { - super.addErrorText(textList); - if (isStructureFormed()) { - if (temperature > 1000) { - textList.add(TextComponentUtil.translationWithColor(TextFormatting.RED, + protected void configureErrorText(MultiblockUIBuilder builder) { + super.configureErrorText(builder); + builder.addCustom((manager, syncer) -> { + if (!isStructureFormed()) return; + + if (syncer.syncDouble(temperature) > 1000) { + manager.add(KeyUtil.lang(TextFormatting.RED, "gregtech.multiblock.hpca.error_temperature")); } - hpcaHandler.addErrors(textList); - } + hpcaHandler.addErrors(manager, syncer); + }); } @Override @@ -465,7 +470,7 @@ public void addInformation(ItemStack stack, @Nullable World world, @NotNull List } @Override - protected boolean shouldShowVoidingModeButton() { + public boolean shouldShowVoidingModeButton() { return false; } @@ -529,40 +534,53 @@ public T getCapability(Capability capability, EnumFacing side) { } @Override - public int getNumProgressBars() { + public int getProgressBarCount() { return 2; } @Override - public double getFillPercentage(int index) { - return index == 0 ? 1.0 * hpcaHandler.cachedCWUt / hpcaHandler.getMaxCWUt() : - Math.min(1.0, temperature / DAMAGE_TEMPERATURE); - } + public void registerBars(List> bars, PanelSyncManager syncManager) { + IntSyncValue currentCWUtValue = new IntSyncValue(() -> hpcaHandler.cachedCWUt); + IntSyncValue maxCWUtValue = new IntSyncValue(hpcaHandler::getMaxCWUt); + syncManager.syncValue("current_cwut", currentCWUtValue); + syncManager.syncValue("max_cwut", maxCWUtValue); + DoubleSyncValue temperatureValue = new DoubleSyncValue(() -> temperature); + syncManager.syncValue("temperature", temperatureValue); - @Override - public TextureArea getProgressBarTexture(int index) { - return index == 0 ? GuiTextures.PROGRESS_BAR_HPCA_COMPUTATION : GuiTextures.PROGRESS_BAR_FUSION_HEAT; - } + bars.add(barTest -> barTest + .progress(() -> 1.0 * currentCWUtValue.getIntValue() / maxCWUtValue.getIntValue()) + .texture(GTGuiTextures.PROGRESS_BAR_HPCA_COMPUTATION) + .tooltipBuilder(t -> { + if (isStructureFormed()) { + t.addLine(IKey.lang("gregtech.multiblock.hpca.computation", + currentCWUtValue.getIntValue(), maxCWUtValue.getIntValue())); + } else { + t.addLine(IKey.lang("gregtech.multiblock.invalid_structure")); + } + })); - @Override - public void addBarHoverText(List hoverList, int index) { - if (index == 0) { - ITextComponent cwutInfo = TextComponentUtil.stringWithColor( - TextFormatting.AQUA, - hpcaHandler.cachedCWUt + " / " + hpcaHandler.getMaxCWUt() + " CWU/t"); - hoverList.add(TextComponentUtil.translationWithColor( - TextFormatting.GRAY, - "gregtech.multiblock.hpca.computation", - cwutInfo)); - } else { - ITextComponent tempInfo = TextComponentUtil.stringWithColor( - getDisplayTemperatureColor(), - Math.round(temperature / 10.0D) + "°C"); - hoverList.add(TextComponentUtil.translationWithColor( - TextFormatting.GRAY, - "gregtech.multiblock.hpca.temperature", - tempInfo)); - } + bars.add(barTest -> barTest + .progress(() -> Math.min(1.0, temperatureValue.getDoubleValue() / DAMAGE_TEMPERATURE)) + .texture(GTGuiTextures.PROGRESS_BAR_FUSION_HEAT) + .tooltipBuilder(t -> { + if (isStructureFormed()) { + double temp = temperatureValue.getDoubleValue(); + int degrees = (int) Math.round(temp / 10.0); + + TextFormatting color; + if (temp < 500) { + color = TextFormatting.GREEN; + } else if (temp < 750) { + color = TextFormatting.YELLOW; + } else { + color = TextFormatting.RED; + } + + t.addLine(IKey.lang("gregtech.multiblock.hpca.temperature", degrees).style(color)); + } else { + t.addLine(IKey.lang("gregtech.multiblock.invalid_structure")); + } + })); } // Handles the logic of this structure's specific HPCA component grid @@ -834,83 +852,96 @@ public int getMaxCoolantDemand() { return maxCoolant; } - public void addInfo(List textList) { + public void addInfo(KeyManager manager, UISyncer syncer) { // Max Computation - ITextComponent data = TextComponentUtil.stringWithColor(TextFormatting.AQUA, - Integer.toString(getMaxCWUt())); - textList.add(TextComponentUtil.translationWithColor(TextFormatting.GRAY, + IKey data = KeyUtil.number(TextFormatting.AQUA, syncer.syncInt(getMaxCWUt())); + manager.add(KeyUtil.lang(TextFormatting.GRAY, "gregtech.multiblock.hpca.info_max_computation", data)); + int coolingAmt = syncer.syncInt(getMaxCoolingAmount()); + int coolingDemand = syncer.syncInt(getMaxCoolingDemand()); + int coolantNeeded = syncer.syncInt(getMaxCoolantDemand()); + // Cooling - TextFormatting coolingColor = getMaxCoolingAmount() < getMaxCoolingDemand() ? TextFormatting.RED : + TextFormatting coolingColor = coolingAmt < coolingDemand ? TextFormatting.RED : TextFormatting.GREEN; - data = TextComponentUtil.stringWithColor(coolingColor, Integer.toString(getMaxCoolingDemand())); - textList.add(TextComponentUtil.translationWithColor(TextFormatting.GRAY, + data = KeyUtil.number(coolingColor, coolingDemand); + manager.add(KeyUtil.lang(TextFormatting.GRAY, "gregtech.multiblock.hpca.info_max_cooling_demand", data)); - data = TextComponentUtil.stringWithColor(coolingColor, Integer.toString(getMaxCoolingAmount())); - textList.add(TextComponentUtil.translationWithColor(TextFormatting.GRAY, + data = KeyUtil.number(coolingColor, coolingAmt); + manager.add(KeyUtil.lang(TextFormatting.GRAY, "gregtech.multiblock.hpca.info_max_cooling_available", data)); // Coolant Required - if (getMaxCoolantDemand() > 0) { - data = TextComponentUtil.stringWithColor( - TextFormatting.YELLOW, - getMaxCoolantDemand() + "L "); - ITextComponent coolantName = TextComponentUtil.translationWithColor(TextFormatting.YELLOW, + if (coolantNeeded > 0) { + data = KeyUtil.number(TextFormatting.YELLOW, coolantNeeded, "L "); + IKey coolantName = KeyUtil.lang(TextFormatting.YELLOW, "gregtech.multiblock.hpca.info_coolant_name"); - data.appendSibling(coolantName); + data = IKey.comp(data, coolantName); } else { - data = TextComponentUtil.stringWithColor(TextFormatting.GREEN, "0"); + data = KeyUtil.string(TextFormatting.GREEN, "0"); } - textList.add(TextComponentUtil.translationWithColor(TextFormatting.GRAY, + + manager.add(KeyUtil.lang(TextFormatting.GRAY, "gregtech.multiblock.hpca.info_max_coolant_required", data)); // Bridging - if (numBridges > 0) { - textList.add(TextComponentUtil.translationWithColor(TextFormatting.GREEN, + if (syncer.syncInt(numBridges) > 0) { + manager.add(KeyUtil.lang(TextFormatting.GREEN, "gregtech.multiblock.hpca.info_bridging_enabled")); } else { - textList.add(TextComponentUtil.translationWithColor(TextFormatting.RED, + manager.add(KeyUtil.lang(TextFormatting.RED, "gregtech.multiblock.hpca.info_bridging_disabled")); } } - public void addWarnings(List textList) { - List warnings = new ArrayList<>(); - if (numBridges > 1) { - warnings.add(TextComponentUtil.translationWithColor(TextFormatting.GRAY, + public void addWarnings(KeyManager keyManager, UISyncer syncer) { + List warnings = new ArrayList<>(); + if (syncer.syncInt(numBridges) > 1) { + warnings.add(KeyUtil.lang(TextFormatting.GRAY, "gregtech.multiblock.hpca.warning_multiple_bridges")); } - if (computationProviders.isEmpty()) { - warnings.add(TextComponentUtil.translationWithColor(TextFormatting.GRAY, + if (syncer.syncBoolean(computationProviders.isEmpty())) { + warnings.add(KeyUtil.lang(TextFormatting.GRAY, "gregtech.multiblock.hpca.warning_no_computation")); } - if (getMaxCoolingDemand() > getMaxCoolingAmount()) { - warnings.add(TextComponentUtil.translationWithColor(TextFormatting.GRAY, + if (syncer.syncBoolean(getMaxCoolingDemand() > getMaxCoolingAmount())) { + warnings.add(KeyUtil.lang(TextFormatting.GRAY, "gregtech.multiblock.hpca.warning_low_cooling")); } if (!warnings.isEmpty()) { - textList.add(TextComponentUtil.translationWithColor(TextFormatting.YELLOW, + keyManager.add(KeyUtil.lang(TextFormatting.YELLOW, "gregtech.multiblock.hpca.warning_structure_header")); - textList.addAll(warnings); + keyManager.addAll(warnings); } } - public void addErrors(List textList) { - if (components.stream().anyMatch(IHPCAComponentHatch::isDamaged)) { - textList.add(TextComponentUtil.translationWithColor(TextFormatting.RED, - "gregtech.multiblock.hpca.error_damaged")); + public void addErrors(KeyManager keyManager, UISyncer syncer) { + for (IHPCAComponentHatch component : components) { + if (syncer.syncBoolean(component.isDamaged())) { + keyManager.add(KeyUtil.lang(TextFormatting.RED, + "gregtech.multiblock.hpca.error_damaged")); + return; + } } } - public TextureArea getComponentTexture(int index) { + public UITexture getComponentTexture(int index) { if (components.size() <= index) { - return GuiTextures.BLANK_TRANSPARENT; + return GTGuiTextures.BLANK_TRANSPARENT; } return components.get(index).getComponentIcon(); } + public IKey getComponentKey(int index) { + if (components.size() <= index) { + return IKey.EMPTY; + } + + return IKey.lang(components.get(index).getTileName()); + } + public void tryGatherClientComponents(World world, BlockPos pos, EnumFacing frontFacing, EnumFacing upwardsFacing, boolean flip) { EnumFacing relativeUp = RelativeDirection.UP.getRelativeFacing(frontFacing, upwardsFacing, flip); diff --git a/src/main/java/gregtech/common/metatileentities/multi/electric/MetaTileEntityLargeMiner.java b/src/main/java/gregtech/common/metatileentities/multi/electric/MetaTileEntityLargeMiner.java index cc2b78fa6ba..8128e1fa525 100644 --- a/src/main/java/gregtech/common/metatileentities/multi/electric/MetaTileEntityLargeMiner.java +++ b/src/main/java/gregtech/common/metatileentities/multi/electric/MetaTileEntityLargeMiner.java @@ -6,19 +6,16 @@ import gregtech.api.capability.impl.FluidTankList; import gregtech.api.capability.impl.ItemHandlerList; import gregtech.api.capability.impl.miner.MultiblockMinerLogic; -import gregtech.api.gui.GuiTextures; -import gregtech.api.gui.ModularUI; -import gregtech.api.gui.Widget; -import gregtech.api.gui.widgets.AdvancedTextWidget; -import gregtech.api.gui.widgets.ImageCycleButtonWidget; import gregtech.api.items.itemhandlers.GTItemStackHandler; import gregtech.api.metatileentity.IDataInfoProvider; import gregtech.api.metatileentity.MetaTileEntity; import gregtech.api.metatileentity.interfaces.IGregTechTileEntity; import gregtech.api.metatileentity.multiblock.IMultiblockPart; import gregtech.api.metatileentity.multiblock.MultiblockAbility; -import gregtech.api.metatileentity.multiblock.MultiblockDisplayText; import gregtech.api.metatileentity.multiblock.MultiblockWithDisplayBase; +import gregtech.api.metatileentity.multiblock.ui.MultiblockUIBuilder; +import gregtech.api.metatileentity.multiblock.ui.MultiblockUIFactory; +import gregtech.api.mui.GTGuiTextures; import gregtech.api.pattern.BlockPattern; import gregtech.api.pattern.FactoryBlockPattern; import gregtech.api.pattern.PatternMatchContext; @@ -27,7 +24,7 @@ import gregtech.api.unification.material.Material; import gregtech.api.unification.material.Materials; import gregtech.api.util.GTUtility; -import gregtech.api.util.TextComponentUtil; +import gregtech.api.util.KeyUtil; import gregtech.client.renderer.ICubeRenderer; import gregtech.client.renderer.texture.Textures; import gregtech.common.blocks.BlockMetalCasing; @@ -57,6 +54,10 @@ import codechicken.lib.render.CCRenderState; import codechicken.lib.render.pipeline.IVertexOperation; import codechicken.lib.vec.Matrix4; +import com.cleanroommc.modularui.api.drawable.IKey; +import com.cleanroommc.modularui.api.widget.Interactable; +import com.cleanroommc.modularui.value.sync.IntSyncValue; +import com.cleanroommc.modularui.widgets.CycleButtonWidget; import com.google.common.collect.Lists; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; @@ -232,79 +233,91 @@ public void addToolUsages(ItemStack stack, @Nullable World world, List t } @Override - protected void addDisplayText(List textList) { - super.addDisplayText(textList); + protected MultiblockUIFactory createUIFactory() { + return super.createUIFactory() + .createFlexButton((posGuiData, panelSyncManager) -> { + IntSyncValue buttonSync = new IntSyncValue(this::getCurrentMode, this::setCurrentMode); - if (this.isStructureFormed()) { - if (energyContainer != null && energyContainer.getEnergyCapacity() > 0) { - int energyContainer = getEnergyTier(); - long maxVoltage = GTValues.V[energyContainer]; - String voltageName = GTValues.VNF[energyContainer]; - textList.add(new TextComponentTranslation("gregtech.multiblock.max_energy_per_tick", maxVoltage, - voltageName)); - } - - int workingAreaChunks = this.minerLogic.getCurrentRadius() * 2 / CHUNK_LENGTH; - int workingArea = getWorkingArea(minerLogic.getCurrentRadius()); - textList.add(new TextComponentTranslation("gregtech.machine.miner.startx", - this.minerLogic.getX().get() == Integer.MAX_VALUE ? 0 : this.minerLogic.getX().get())); - textList.add(new TextComponentTranslation("gregtech.machine.miner.starty", - this.minerLogic.getY().get() == Integer.MAX_VALUE ? 0 : this.minerLogic.getY().get())); - textList.add(new TextComponentTranslation("gregtech.machine.miner.startz", - this.minerLogic.getZ().get() == Integer.MAX_VALUE ? 0 : this.minerLogic.getZ().get())); - if (this.minerLogic.isChunkMode()) { - textList.add(new TextComponentTranslation("gregtech.machine.miner.working_area_chunks", - workingAreaChunks, workingAreaChunks)); - } else { - textList.add( - new TextComponentTranslation("gregtech.machine.miner.working_area", workingArea, workingArea)); - } - if (this.minerLogic.isDone()) - textList.add(new TextComponentTranslation("gregtech.machine.miner.done") - .setStyle(new Style().setColor(TextFormatting.GREEN))); - else if (this.minerLogic.isWorking()) - textList.add(new TextComponentTranslation("gregtech.machine.miner.working") - .setStyle(new Style().setColor(TextFormatting.GOLD))); - else if (!this.isWorkingEnabled()) - textList.add(new TextComponentTranslation("gregtech.multiblock.work_paused")); - } - } + return new CycleButtonWidget() { - private void addDisplayText2(List textList) { - if (this.isStructureFormed()) { - ITextComponent mCoords = new TextComponentString(" ") - .appendSibling(new TextComponentTranslation("gregtech.machine.miner.minex", - this.minerLogic.getMineX().get())) - .appendText("\n ") - .appendSibling(new TextComponentTranslation("gregtech.machine.miner.miney", - this.minerLogic.getMineY().get())) - .appendText("\n ") - .appendSibling(new TextComponentTranslation("gregtech.machine.miner.minez", - this.minerLogic.getMineZ().get())); - textList.add(mCoords); - } + @Override + public @NotNull Result onMousePressed(int mouseButton) { + if (minerLogic.isWorking()) { + Interactable.playButtonClickSound(); + return Result.IGNORE; + } else { + return super.onMousePressed(mouseButton); + } + } + } + .stateCount(4) + .value(buttonSync) + .stateBackground(GTGuiTextures.BUTTON_MINER_MODES) + .addTooltip(0, IKey.lang("gregtech.multiblock.miner.neither_mode")) + .addTooltip(1, IKey.lang("gregtech.multiblock.miner.chunk_mode")) + .addTooltip(2, IKey.lang("gregtech.multiblock.miner.silk_touch_mode")) + .addTooltip(3, IKey.lang("gregtech.multiblock.miner.both_modes")); + }); } @Override - protected void addWarningText(List textList) { - MultiblockDisplayText.builder(textList, isStructureFormed(), false) - .addLowPowerLine(isStructureFormed() && !drainEnergy(true)) - .addCustom(tl -> { - if (isStructureFormed() && isInventoryFull) { - tl.add(TextComponentUtil.translationWithColor( - TextFormatting.YELLOW, - "gregtech.machine.miner.invfull")); + protected void configureDisplayText(MultiblockUIBuilder builder) { + builder.setWorkingStatus(minerLogic.isWorkingEnabled(), minerLogic.isActive()) + .addEnergyUsageLine(energyContainer) + .addCustom((list, syncer) -> { + if (isStructureFormed()) { + int workingAreaChunks = syncer.syncInt(this.minerLogic.getCurrentRadius() * 2 / CHUNK_LENGTH); + int workingArea = syncer.syncInt(getWorkingArea(minerLogic.getCurrentRadius())); + + list.add(KeyUtil.lang(TextFormatting.GRAY, "gregtech.machine.miner.mining_at")); + list.add(KeyUtil.lang(TextFormatting.GRAY, "gregtech.machine.miner.mining_pos", + syncer.syncInt(minerLogic.getMineX().get()), + syncer.syncInt(minerLogic.getMineY().get()), + syncer.syncInt(minerLogic.getMineZ().get()))); + + if (syncer.syncBoolean(minerLogic.isChunkMode())) { + list.add(KeyUtil.lang(TextFormatting.GRAY, "gregtech.machine.miner.working_area_chunks", + workingAreaChunks, + workingAreaChunks)); + } else { + list.add(KeyUtil.lang(TextFormatting.GRAY, "gregtech.machine.miner.working_area", + workingArea, workingArea)); + } + + if (syncer.syncBoolean(minerLogic.isDone())) { + list.add(KeyUtil.lang(TextFormatting.GREEN, "gregtech.machine.miner.done")); + } else if (syncer.syncBoolean(minerLogic.isWorking())) { + list.add(KeyUtil.lang(TextFormatting.GOLD, "gregtech.machine.miner.working")); + } else if (!syncer.syncBoolean(isWorkingEnabled())) { + list.add(KeyUtil.lang(TextFormatting.GRAY, "gregtech.multiblock.work_paused")); + } } }); } @Override - protected void addErrorText(List textList) { - super.addErrorText(textList); - if (isStructureFormed() && !drainFluid(true)) { - textList.add(TextComponentUtil.translationWithColor(TextFormatting.RED, - "gregtech.machine.miner.multi.needsfluid")); + protected void configureErrorText(MultiblockUIBuilder builder) { + super.configureErrorText(builder); + builder.addCustom((list, syncer) -> { + if (isStructureFormed() && syncer.syncBoolean(() -> !drainFluid(false))) { + list.add(KeyUtil.lang(TextFormatting.RED, "gregtech.machine.miner.multi.needsfluid")); + } + }); + } + + @Override + protected void configureWarningText(MultiblockUIBuilder builder) { + boolean lowPower = false; + if (isStructureFormed() && !getWorld().isRemote) { + lowPower = !drainEnergy(true); } + builder.addLowPowerLine(lowPower) + .addCustom((list, syncer) -> { + if (isStructureFormed() && syncer.syncBoolean(isInventoryFull)) { + list.add(KeyUtil.lang(TextFormatting.YELLOW, "gregtech.machine.miner.invfull")); + } + }); + super.configureWarningText(builder); } public IBlockState getCasingState() { @@ -383,14 +396,6 @@ public long getMaxVoltage() { return GTValues.V[GTUtility.getTierByVoltage(energyContainer.getInputVoltage())]; } - @Override - protected ModularUI.Builder createUITemplate(EntityPlayer entityPlayer) { - ModularUI.Builder builder = super.createUITemplate(entityPlayer); - builder.widget(new AdvancedTextWidget(63, 31, this::addDisplayText2, 0xFFFFFF) - .setMaxWidthLimit(68).setClickHandler(this::handleDisplayClick)); - return builder; - } - // used for UI private int getCurrentMode() { // 0 -> not chunk mode, not silk touch mode @@ -431,18 +436,6 @@ private void setCurrentMode(int mode) { } } - @Override - protected @NotNull Widget getFlexButton(int x, int y, int width, int height) { - return new ImageCycleButtonWidget(x, y, width, height, GuiTextures.BUTTON_MINER_MODES, 4, this::getCurrentMode, - this::setCurrentMode) - .setTooltipHoverString(mode -> switch (mode) { - case 0 -> "gregtech.multiblock.miner.neither_mode"; - case 1 -> "gregtech.multiblock.miner.chunk_mode"; - case 2 -> "gregtech.multiblock.miner.silk_touch_mode"; - default -> "gregtech.multiblock.miner.both_modes"; - }); - } - @Override public boolean onScrewdriverClick(EntityPlayer playerIn, EnumHand hand, EnumFacing facing, CuboidRayTraceResult hitResult) { @@ -550,7 +543,7 @@ public List getDataInfo() { } @Override - protected boolean shouldShowVoidingModeButton() { + public boolean shouldShowVoidingModeButton() { return false; } 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 71fca7ba8d4..e7d8dd7a045 100644 --- a/src/main/java/gregtech/common/metatileentities/multi/electric/MetaTileEntityMultiSmelter.java +++ b/src/main/java/gregtech/common/metatileentities/multi/electric/MetaTileEntityMultiSmelter.java @@ -6,9 +6,9 @@ import gregtech.api.metatileentity.interfaces.IGregTechTileEntity; import gregtech.api.metatileentity.multiblock.IMultiblockPart; import gregtech.api.metatileentity.multiblock.MultiblockAbility; -import gregtech.api.metatileentity.multiblock.MultiblockDisplayText; import gregtech.api.metatileentity.multiblock.ParallelLogicType; import gregtech.api.metatileentity.multiblock.RecipeMapMultiblockController; +import gregtech.api.metatileentity.multiblock.ui.MultiblockUIBuilder; import gregtech.api.pattern.BlockPattern; import gregtech.api.pattern.FactoryBlockPattern; import gregtech.api.pattern.PatternMatchContext; @@ -19,8 +19,7 @@ import gregtech.api.recipes.machines.RecipeMapFurnace; import gregtech.api.recipes.properties.RecipePropertyStorage; import gregtech.api.util.GTUtility; -import gregtech.api.util.TextComponentUtil; -import gregtech.api.util.TextFormattingUtil; +import gregtech.api.util.KeyUtil; import gregtech.client.renderer.ICubeRenderer; import gregtech.client.renderer.texture.Textures; import gregtech.common.blocks.BlockMetalCasing.MetalCasingType; @@ -31,15 +30,13 @@ import net.minecraft.block.state.IBlockState; import net.minecraft.util.ResourceLocation; import net.minecraft.util.SoundEvent; -import net.minecraft.util.text.ITextComponent; import net.minecraft.util.text.TextFormatting; import net.minecraftforge.fml.relauncher.Side; import net.minecraftforge.fml.relauncher.SideOnly; +import com.cleanroommc.modularui.api.drawable.IKey; import org.jetbrains.annotations.NotNull; -import java.util.List; - import static gregtech.api.recipes.logic.OverclockingLogic.standardOC; public class MetaTileEntityMultiSmelter extends RecipeMapMultiblockController { @@ -58,63 +55,53 @@ public MetaTileEntity createMetaTileEntity(IGregTechTileEntity tileEntity) { } @Override - protected void addDisplayText(List textList) { - MultiblockDisplayText.builder(textList, isStructureFormed()) - .setWorkingStatus(recipeMapWorkable.isWorkingEnabled(), recipeMapWorkable.isActive()) - .addEnergyUsageLine(recipeMapWorkable.getEnergyContainer()) + protected void configureDisplayText(MultiblockUIBuilder builder) { + builder.setWorkingStatus(recipeMapWorkable.isWorkingEnabled(), recipeMapWorkable.isActive()) + .addEnergyUsageLine(getEnergyContainer()) .addEnergyTierLine(GTUtility.getTierByVoltage(recipeMapWorkable.getMaxVoltage())) - .addCustom(tl -> { - if (isStructureFormed()) { - // Heating coil discount - if (heatingCoilDiscount > 1) { - ITextComponent coilDiscount = TextComponentUtil.stringWithColor( - TextFormatting.AQUA, - TextFormattingUtil.formatNumbers(100.0 / heatingCoilDiscount) + "%"); - - ITextComponent base = TextComponentUtil.translationWithColor( - TextFormatting.GRAY, - "gregtech.multiblock.multi_furnace.heating_coil_discount", - coilDiscount); - - ITextComponent hoverText = TextComponentUtil.translationWithColor( - TextFormatting.GRAY, - "gregtech.multiblock.multi_furnace.heating_coil_discount_hover"); - - TextComponentUtil.setHover(base, hoverText); - tl.add(base); - } - - // Custom parallels line so we can have a hover text - if (recipeMapWorkable.getParallelLimit() > 1) { - ITextComponent parallels = TextComponentUtil.stringWithColor( - TextFormatting.DARK_PURPLE, - TextFormattingUtil.formatNumbers(recipeMapWorkable.getParallelLimit())); - ITextComponent bodyText = TextComponentUtil.translationWithColor( - TextFormatting.GRAY, - "gregtech.multiblock.parallel", - parallels); - ITextComponent hoverText = TextComponentUtil.translationWithColor( - TextFormatting.GRAY, - "gregtech.multiblock.multi_furnace.parallel_hover"); - tl.add(TextComponentUtil.setHover(bodyText, hoverText)); - } + .addCustom((richText, syncer) -> { + if (!isStructureFormed()) return; + + int discount = syncer.syncInt(heatingCoilDiscount); + if (discount > 1) { + IKey coilDiscount = KeyUtil.number(TextFormatting.AQUA, + (long) (100.0 / discount), "%"); + + IKey base = KeyUtil.lang(TextFormatting.GRAY, + "gregtech.multiblock.multi_furnace.heating_coil_discount", + coilDiscount); + + IKey hoverText = KeyUtil.lang(TextFormatting.GRAY, + "gregtech.multiblock.multi_furnace.heating_coil_discount_hover"); + + richText.add(KeyUtil.setHover(base, hoverText)); + } + + int pLimit = syncer.syncInt(recipeMapWorkable.getParallelLimit()); + if (pLimit > 0) { + IKey parallels = KeyUtil.number(TextFormatting.DARK_PURPLE, pLimit); + + IKey bodyText = KeyUtil.lang(TextFormatting.GRAY, + "gregtech.multiblock.parallel", + parallels); + + IKey hoverText = KeyUtil.lang(TextFormatting.GRAY, + "gregtech.multiblock.multi_furnace.parallel_hover"); + + richText.add(KeyUtil.setHover(bodyText, hoverText)); } }) .addWorkingStatusLine() - .addProgressLine(recipeMapWorkable.getProgressPercent()); + .addProgressLine(recipeMapWorkable.getProgress(), recipeMapWorkable.getMaxProgress()) + .addRecipeOutputLine(recipeMapWorkable); } @Override protected void formStructure(PatternMatchContext context) { super.formStructure(context); - Object coilType = context.get("CoilType"); - if (coilType instanceof IHeatingCoilBlockStats) { - this.heatingCoilLevel = ((IHeatingCoilBlockStats) coilType).getLevel(); - this.heatingCoilDiscount = ((IHeatingCoilBlockStats) coilType).getEnergyDiscount(); - } else { - this.heatingCoilLevel = CoilType.CUPRONICKEL.getLevel(); - this.heatingCoilDiscount = CoilType.CUPRONICKEL.getEnergyDiscount(); - } + IHeatingCoilBlockStats coilType = context.getOrDefault("CoilType", CoilType.CUPRONICKEL); + this.heatingCoilLevel = coilType.getLevel(); + this.heatingCoilDiscount = coilType.getEnergyDiscount(); } @Override diff --git a/src/main/java/gregtech/common/metatileentities/multi/electric/MetaTileEntityNetworkSwitch.java b/src/main/java/gregtech/common/metatileentities/multi/electric/MetaTileEntityNetworkSwitch.java index fc49827d51b..4ad8d1fbe4c 100644 --- a/src/main/java/gregtech/common/metatileentities/multi/electric/MetaTileEntityNetworkSwitch.java +++ b/src/main/java/gregtech/common/metatileentities/multi/electric/MetaTileEntityNetworkSwitch.java @@ -8,11 +8,11 @@ import gregtech.api.metatileentity.interfaces.IGregTechTileEntity; import gregtech.api.metatileentity.multiblock.IMultiblockPart; import gregtech.api.metatileentity.multiblock.MultiblockAbility; -import gregtech.api.metatileentity.multiblock.MultiblockDisplayText; +import gregtech.api.metatileentity.multiblock.ui.MultiblockUIBuilder; import gregtech.api.pattern.BlockPattern; import gregtech.api.pattern.FactoryBlockPattern; import gregtech.api.pattern.PatternMatchContext; -import gregtech.api.util.TextComponentUtil; +import gregtech.api.util.KeyUtil; import gregtech.api.util.TextFormattingUtil; import gregtech.client.renderer.ICubeRenderer; import gregtech.client.renderer.texture.Textures; @@ -23,7 +23,6 @@ import net.minecraft.client.resources.I18n; import net.minecraft.item.ItemStack; import net.minecraft.util.ResourceLocation; -import net.minecraft.util.text.ITextComponent; import net.minecraft.util.text.TextFormatting; import net.minecraft.world.World; import net.minecraftforge.fml.relauncher.Side; @@ -145,9 +144,8 @@ public void addInformation(ItemStack stack, @Nullable World world, @NotNull List } @Override - protected void addDisplayText(List textList) { - MultiblockDisplayText.builder(textList, isStructureFormed()) - .setWorkingStatus(true, isActive() && isWorkingEnabled()) // transform into two-state system for display + protected void configureDisplayText(MultiblockUIBuilder builder) { + builder.setWorkingStatus(true, isActive() && isWorkingEnabled()) // transform into two-state system for display .setWorkingStatusKeys( "gregtech.multiblock.idling", "gregtech.multiblock.idling", @@ -158,13 +156,13 @@ protected void addDisplayText(List textList) { } @Override - protected void addWarningText(List textList) { - super.addWarningText(textList); - if (isStructureFormed() && computationHandler.hasNonBridgingConnections()) { - textList.add(TextComponentUtil.translationWithColor( - TextFormatting.YELLOW, - "gregtech.multiblock.computation.non_bridging.detailed")); - } + protected void configureWarningText(MultiblockUIBuilder builder) { + super.configureWarningText(builder); + builder.addCustom((list, syncer) -> { + if (isStructureFormed() && syncer.syncBoolean(computationHandler.hasNonBridgingConnections())) { + list.add(KeyUtil.lang(TextFormatting.YELLOW, "gregtech.multiblock.computation.non_bridging.detailed")); + } + }); } /** Handles computation load across multiple receivers and to multiple transmitters. */ diff --git a/src/main/java/gregtech/common/metatileentities/multi/electric/MetaTileEntityPowerSubstation.java b/src/main/java/gregtech/common/metatileentities/multi/electric/MetaTileEntityPowerSubstation.java index c189c803800..96a6121b8c9 100644 --- a/src/main/java/gregtech/common/metatileentities/multi/electric/MetaTileEntityPowerSubstation.java +++ b/src/main/java/gregtech/common/metatileentities/multi/electric/MetaTileEntityPowerSubstation.java @@ -11,17 +11,20 @@ import gregtech.api.metatileentity.interfaces.IGregTechTileEntity; import gregtech.api.metatileentity.multiblock.IBatteryData; import gregtech.api.metatileentity.multiblock.IMultiblockPart; -import gregtech.api.metatileentity.multiblock.IProgressBarMultiblock; import gregtech.api.metatileentity.multiblock.MultiblockAbility; -import gregtech.api.metatileentity.multiblock.MultiblockDisplayText; import gregtech.api.metatileentity.multiblock.MultiblockWithDisplayBase; +import gregtech.api.metatileentity.multiblock.ProgressBarMultiblock; +import gregtech.api.metatileentity.multiblock.ui.MultiblockUIBuilder; +import gregtech.api.metatileentity.multiblock.ui.TemplateBarBuilder; +import gregtech.api.mui.GTGuiTextures; +import gregtech.api.mui.sync.BigIntegerSyncValue; import gregtech.api.pattern.BlockPattern; import gregtech.api.pattern.FactoryBlockPattern; import gregtech.api.pattern.MultiblockShapeInfo; import gregtech.api.pattern.PatternMatchContext; import gregtech.api.pattern.TraceabilityPredicate; import gregtech.api.util.BlockInfo; -import gregtech.api.util.TextComponentUtil; +import gregtech.api.util.KeyUtil; import gregtech.api.util.TextFormattingUtil; import gregtech.client.renderer.ICubeRenderer; import gregtech.client.renderer.texture.Textures; @@ -40,8 +43,6 @@ import net.minecraft.network.PacketBuffer; import net.minecraft.util.EnumFacing; import net.minecraft.util.ResourceLocation; -import net.minecraft.util.text.ITextComponent; -import net.minecraft.util.text.TextComponentTranslation; import net.minecraft.util.text.TextFormatting; import net.minecraft.world.World; import net.minecraftforge.common.capabilities.Capability; @@ -52,6 +53,8 @@ import codechicken.lib.render.CCRenderState; import codechicken.lib.render.pipeline.IVertexOperation; import codechicken.lib.vec.Matrix4; +import com.cleanroommc.modularui.api.drawable.IKey; +import com.cleanroommc.modularui.value.sync.PanelSyncManager; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import org.jetbrains.annotations.VisibleForTesting; @@ -64,11 +67,12 @@ import java.util.List; import java.util.Map; import java.util.function.Supplier; +import java.util.function.UnaryOperator; import static gregtech.api.util.RelativeDirection.*; public class MetaTileEntityPowerSubstation extends MultiblockWithDisplayBase - implements IControllable, IProgressBarMultiblock { + implements IControllable, ProgressBarMultiblock { // Structure Constants public static final int MAX_BATTERY_LAYERS = 18; @@ -233,7 +237,7 @@ public void setWorkingEnabled(boolean isWorkingAllowed) { } @Override - protected boolean shouldShowVoidingModeButton() { + public boolean shouldShowVoidingModeButton() { return false; } @@ -340,112 +344,101 @@ public void renderMetaTileEntity(CCRenderState renderState, Matrix4 translation, } @Override - protected void addDisplayText(List textList) { - MultiblockDisplayText.builder(textList, isStructureFormed()) - .setWorkingStatus(true, isActive() && isWorkingEnabled()) // transform into two-state system for display - .setWorkingStatusKeys( - "gregtech.multiblock.idling", - "gregtech.multiblock.idling", - "gregtech.machine.active_transformer.routing") - .addCustom(tl -> { - if (isStructureFormed() && energyBank != null) { - BigInteger energyStored = energyBank.getStored(); - BigInteger energyCapacity = energyBank.getCapacity(); - - // Stored EU line - ITextComponent storedFormatted = TextComponentUtil.stringWithColor( - TextFormatting.GOLD, - TextFormattingUtil.formatNumbers(energyStored) + " EU"); - tl.add(TextComponentUtil.translationWithColor( - TextFormatting.GRAY, - "gregtech.multiblock.power_substation.stored", - storedFormatted)); - - // EU Capacity line - ITextComponent capacityFormatted = TextComponentUtil.stringWithColor( - TextFormatting.GOLD, - TextFormattingUtil.formatNumbers(energyCapacity) + " EU"); - tl.add(TextComponentUtil.translationWithColor( - TextFormatting.GRAY, - "gregtech.multiblock.power_substation.capacity", - capacityFormatted)); - - // Passive Drain line - ITextComponent passiveDrain = TextComponentUtil.stringWithColor( - TextFormatting.DARK_RED, - TextFormattingUtil.formatNumbers(getPassiveDrain()) + " EU/t"); - tl.add(TextComponentUtil.translationWithColor( - TextFormatting.GRAY, - "gregtech.multiblock.power_substation.passive_drain", - passiveDrain)); - - // Average EU IN line - ITextComponent avgValue = TextComponentUtil.stringWithColor( - TextFormatting.GREEN, - TextFormattingUtil.formatNumbers(averageInLastSec) + " EU/t"); - ITextComponent base = TextComponentUtil.translationWithColor( - TextFormatting.GRAY, - "gregtech.multiblock.power_substation.average_in", - avgValue); - ITextComponent hover = TextComponentUtil.translationWithColor( - TextFormatting.GRAY, - "gregtech.multiblock.power_substation.average_in_hover"); - tl.add(TextComponentUtil.setHover(base, hover)); - - // Average EU OUT line - avgValue = TextComponentUtil.stringWithColor( - TextFormatting.RED, - TextFormattingUtil.formatNumbers(averageOutLastSec) + " EU/t"); - base = TextComponentUtil.translationWithColor( - TextFormatting.GRAY, - "gregtech.multiblock.power_substation.average_out", - avgValue); - hover = TextComponentUtil.translationWithColor( - TextFormatting.GRAY, - "gregtech.multiblock.power_substation.average_out_hover"); - tl.add(TextComponentUtil.setHover(base, hover)); - - // Time to fill/drain line - if (averageInLastSec > averageOutLastSec) { - ITextComponent timeToFill = getTimeToFillDrainText(energyCapacity.subtract(energyStored) - .divide(BigInteger.valueOf((averageInLastSec - averageOutLastSec) * 20))); - TextComponentUtil.setColor(timeToFill, TextFormatting.GREEN); - tl.add(TextComponentUtil.translationWithColor( - TextFormatting.GRAY, - "gregtech.multiblock.power_substation.time_to_fill", - timeToFill)); - } else if (averageInLastSec < averageOutLastSec) { - ITextComponent timeToDrain = getTimeToFillDrainText( - energyStored.divide(BigInteger.valueOf( - (averageOutLastSec - averageInLastSec) * 20))); - TextComponentUtil.setColor(timeToDrain, TextFormatting.RED); - tl.add(TextComponentUtil.translationWithColor( - TextFormatting.GRAY, - "gregtech.multiblock.power_substation.time_to_drain", - timeToDrain)); - } - } - }) - .addWorkingStatusLine(); + protected void configureDisplayText(MultiblockUIBuilder builder) { + builder.structureFormed(isStructureFormed()); + builder.setWorkingStatus(true, isActive() && isWorkingEnabled()); // transform into two-state system for display + builder.setWorkingStatusKeys("gregtech.multiblock.idling", "gregtech.multiblock.idling", + "gregtech.machine.active_transformer.routing"); + builder.addCustom((manager, syncer) -> { + if (isStructureFormed() && syncer.syncBoolean(energyBank != null)) { + BigInteger energyStored = syncer + .syncBigInt(energyBank == null ? BigInteger.ZERO : energyBank.getStored()); + BigInteger energyCapacity = syncer + .syncBigInt(energyBank == null ? BigInteger.ZERO : energyBank.getCapacity()); + + // Stored EU line + IKey storedFormatted = KeyUtil.string( + TextFormattingUtil.formatNumbers(energyStored) + " EU"); + + IKey truncated = KeyUtil.string(TextFormatting.GOLD, + TextFormattingUtil.formatBigIntToCompactString(energyStored, 7) + " EU"); + + IKey bodyStored = (KeyUtil.lang(TextFormatting.GRAY, "gregtech.multiblock.power_substation.stored", + truncated)); + + manager.add(KeyUtil.setHover(bodyStored, storedFormatted)); + + // EU Capacity line + IKey capacityFormatted = KeyUtil.string( + TextFormattingUtil.formatNumbers(energyCapacity) + " EU"); + + IKey capCompact = KeyUtil.string(TextFormatting.GOLD, + TextFormattingUtil.formatBigIntToCompactString(energyCapacity, 7) + " EU"); + + IKey bodyCap = KeyUtil.lang(TextFormatting.GRAY, "gregtech.multiblock.power_substation.capacity", + capCompact); + + manager.add(KeyUtil.setHover(bodyCap, capacityFormatted)); + + // Passive Drain line + IKey passiveDrain = KeyUtil.string(TextFormatting.DARK_RED, + TextFormattingUtil.formatNumbers(syncer.syncLong(getPassiveDrain())) + " EU/t"); + manager.add(KeyUtil.lang(TextFormatting.GRAY, "gregtech.multiblock.power_substation.passive_drain", + passiveDrain)); + + // Average EU IN line + long avgIn = syncer.syncLong(averageInLastSec); + long avgOut = syncer.syncLong(averageOutLastSec); + + IKey avgValue = KeyUtil.number(TextFormatting.GREEN, avgIn, " EU/t"); + IKey base = KeyUtil.lang(TextFormatting.GRAY, "gregtech.multiblock.power_substation.average_in", + avgValue); + IKey hover = KeyUtil.lang(TextFormatting.GRAY, "gregtech.multiblock.power_substation.average_in_hover"); + manager.add(KeyUtil.setHover(base, hover)); + + // Average EU OUT line + avgValue = KeyUtil.number(TextFormatting.RED, avgOut, " EU/t"); + base = KeyUtil.lang(TextFormatting.GRAY, "gregtech.multiblock.power_substation.average_out", avgValue); + hover = KeyUtil.lang(TextFormatting.GRAY, "gregtech.multiblock.power_substation.average_out_hover"); + manager.add(KeyUtil.setHover(base, hover)); + + // Time to fill/drain line + if (avgIn > avgOut) { + IKey timeToFill = getTimeToFillDrainText(energyCapacity.subtract(energyStored) + .divide(BigInteger.valueOf((avgIn - avgOut) * 20))) + .style(TextFormatting.GREEN); + + manager.add(KeyUtil.lang(TextFormatting.GRAY, "gregtech.multiblock.power_substation.time_to_fill", + timeToFill)); + + } else if (avgIn < avgOut) { + IKey timeToDrain = getTimeToFillDrainText( + energyStored.divide(BigInteger.valueOf((avgOut - avgIn) * 20))) + .style(TextFormatting.RED); + + manager.add(KeyUtil.lang(TextFormatting.GRAY, "gregtech.multiblock.power_substation.time_to_drain", + timeToDrain)); + } + } + }).addWorkingStatusLine(); } @Override - protected void addWarningText(List textList) { - super.addWarningText(textList); - if (isStructureFormed()) { - if (averageInLastSec < averageOutLastSec) { // decreasing + protected void configureWarningText(MultiblockUIBuilder builder) { + super.configureWarningText(builder); + builder.addCustom((list, syncer) -> { + if (isStructureFormed() && averageInLastSec < averageOutLastSec) { BigInteger timeToDrainSeconds = energyBank.getStored() .divide(BigInteger.valueOf((averageOutLastSec - averageInLastSec) * 20)); if (timeToDrainSeconds.compareTo(BigInteger.valueOf(60 * 60)) < 0) { // less than 1 hour left - textList.add(TextComponentUtil.translationWithColor( - TextFormatting.YELLOW, + list.add(KeyUtil.lang(TextFormatting.YELLOW, "gregtech.multiblock.power_substation.under_one_hour_left")); } } - } + }); } - private static ITextComponent getTimeToFillDrainText(BigInteger timeToFillSeconds) { + private static IKey getTimeToFillDrainText(BigInteger timeToFillSeconds) { if (timeToFillSeconds.compareTo(BIG_INTEGER_MAX_LONG) > 0) { // too large to represent in a java Duration timeToFillSeconds = BIG_INTEGER_MAX_LONG; @@ -470,10 +463,10 @@ private static ITextComponent getTimeToFillDrainText(BigInteger timeToFillSecond fillTime = duration.toDays() / 365; key = "gregtech.multiblock.power_substation.time_years"; } else { - return new TextComponentTranslation("gregtech.multiblock.power_substation.time_forever"); + return KeyUtil.lang("gregtech.multiblock.power_substation.time_forever"); } - return new TextComponentTranslation(key, TextFormattingUtil.formatNumbers(fillTime)); + return KeyUtil.lang(key, TextFormattingUtil.formatNumbers(fillTime)); } @Override @@ -580,23 +573,31 @@ public long getAverageOutLastSec() { } @Override - public double getFillPercentage(int index) { - if (energyBank == null) return 0; - return energyBank.getStored().doubleValue() / energyBank.getCapacity().doubleValue(); + public int getProgressBarCount() { + return 1; } @Override - public void addBarHoverText(List hoverList, int index) { - String stored = energyBank != null ? TextFormattingUtil.formatNumbers(energyBank.getStored()) : "0"; - String capacity = energyBank != null ? TextFormattingUtil.formatNumbers(energyBank.getCapacity()) : "0"; - - ITextComponent energyInfo = TextComponentUtil.stringWithColor( - TextFormatting.YELLOW, - stored + " / " + capacity + " EU"); - hoverList.add(TextComponentUtil.translationWithColor( - TextFormatting.GRAY, - "gregtech.multiblock.energy_stored", - energyInfo)); + public void registerBars(List> bars, PanelSyncManager syncManager) { + BigIntegerSyncValue energyStoredValue = new BigIntegerSyncValue( + () -> energyBank == null ? BigInteger.ZERO : energyBank.getStored(), null); + BigIntegerSyncValue energyCapacityValue = new BigIntegerSyncValue( + () -> energyBank == null ? BigInteger.ZERO : energyBank.getCapacity(), null); + syncManager.syncValue("energy_stored", energyStoredValue); + syncManager.syncValue("energy_capacity", energyCapacityValue); + + bars.add(b -> b + .progress( + () -> energyStoredValue.getValue().doubleValue() / energyCapacityValue.getValue().doubleValue()) + .texture(GTGuiTextures.PROGRESS_BAR_MULTI_ENERGY_YELLOW) + .tooltipBuilder(t -> { + if (isStructureFormed()) { + t.addLine(IKey.lang("gregtech.multiblock.energy_stored", energyStoredValue.getValue(), + energyCapacityValue.getValue())); + } else { + t.addLine(IKey.lang("gregtech.multiblock.invalid_structure")); + } + })); } public static class PowerStationEnergyBank { 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 0a06b0fb546..fbb5d7faeab 100644 --- a/src/main/java/gregtech/common/metatileentities/multi/electric/MetaTileEntityProcessingArray.java +++ b/src/main/java/gregtech/common/metatileentities/multi/electric/MetaTileEntityProcessingArray.java @@ -14,8 +14,8 @@ import gregtech.api.metatileentity.multiblock.ICleanroomReceiver; import gregtech.api.metatileentity.multiblock.IMultiblockPart; import gregtech.api.metatileentity.multiblock.MultiblockAbility; -import gregtech.api.metatileentity.multiblock.MultiblockDisplayText; import gregtech.api.metatileentity.multiblock.RecipeMapMultiblockController; +import gregtech.api.metatileentity.multiblock.ui.MultiblockUIBuilder; import gregtech.api.pattern.BlockPattern; import gregtech.api.pattern.FactoryBlockPattern; import gregtech.api.pattern.PatternMatchContext; @@ -26,7 +26,7 @@ import gregtech.api.recipes.logic.OCResult; import gregtech.api.recipes.properties.RecipePropertyStorage; import gregtech.api.util.GTUtility; -import gregtech.api.util.TextComponentUtil; +import gregtech.api.util.KeyUtil; import gregtech.api.util.TextFormattingUtil; import gregtech.client.renderer.ICubeRenderer; import gregtech.client.renderer.texture.Textures; @@ -41,14 +41,13 @@ import net.minecraft.item.ItemStack; import net.minecraft.util.ResourceLocation; import net.minecraft.util.SoundEvent; -import net.minecraft.util.text.ITextComponent; -import net.minecraft.util.text.TextComponentString; import net.minecraft.util.text.TextFormatting; import net.minecraft.world.World; import net.minecraftforge.fml.relauncher.Side; import net.minecraftforge.fml.relauncher.SideOnly; import net.minecraftforge.items.IItemHandlerModifiable; +import com.cleanroommc.modularui.api.drawable.IKey; import org.apache.commons.lang3.ArrayUtils; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; @@ -115,63 +114,64 @@ public ICubeRenderer getBaseTexture(IMultiblockPart sourcePart) { } @Override - protected void addDisplayText(List textList) { + protected void configureDisplayText(MultiblockUIBuilder builder) { ProcessingArrayWorkable logic = (ProcessingArrayWorkable) recipeMapWorkable; - MultiblockDisplayText.builder(textList, isStructureFormed()) - .setWorkingStatus(recipeMapWorkable.isWorkingEnabled(), recipeMapWorkable.isActive()) - .addEnergyUsageLine(recipeMapWorkable.getEnergyContainer()) - .addEnergyTierLine(logic.currentMachineStack == ItemStack.EMPTY ? -1 : logic.machineTier) - .addCustom(tl -> { - if (isStructureFormed()) { - - // Machine mode text - // Shared text components for both states - ITextComponent maxMachinesText = TextComponentUtil.stringWithColor(TextFormatting.DARK_PURPLE, - Integer.toString(getMachineLimit())); - maxMachinesText = TextComponentUtil.translationWithColor(TextFormatting.GRAY, - "gregtech.machine.machine_hatch.machines_max", maxMachinesText); - - if (logic.activeRecipeMap == null) { - // No machines in hatch - ITextComponent noneText = TextComponentUtil.translationWithColor(TextFormatting.YELLOW, - "gregtech.machine.machine_hatch.machines_none"); - ITextComponent bodyText = TextComponentUtil.translationWithColor(TextFormatting.GRAY, - "gregtech.machine.machine_hatch.machines", noneText); - ITextComponent hoverText1 = TextComponentUtil.translationWithColor(TextFormatting.GRAY, - "gregtech.machine.machine_hatch.machines_none_hover"); - tl.add(TextComponentUtil.setHover(bodyText, hoverText1, maxMachinesText)); - } else { - // Some amount of machines in hatch - String key = logic.getMachineStack().getTranslationKey(); - ITextComponent mapText = TextComponentUtil.translationWithColor(TextFormatting.DARK_PURPLE, - key + ".name"); - mapText = TextComponentUtil.translationWithColor( - TextFormatting.DARK_PURPLE, - "%sx %s", - logic.getParallelLimit(), mapText); - ITextComponent bodyText = TextComponentUtil.translationWithColor(TextFormatting.GRAY, - "gregtech.machine.machine_hatch.machines", mapText); - ITextComponent voltageName = new TextComponentString(GTValues.VNF[logic.machineTier]); - int amps = logic.getMachineStack().getCount(); - String energyFormatted = TextFormattingUtil - .formatNumbers(GTValues.V[logic.machineTier] * amps); - ITextComponent hoverText = TextComponentUtil.translationWithColor( - TextFormatting.GRAY, - "gregtech.machine.machine_hatch.machines_max_eut", - energyFormatted, amps, voltageName); - tl.add(TextComponentUtil.setHover(bodyText, hoverText, maxMachinesText)); - } - - // Hatch locked status - if (isActive()) { - tl.add(TextComponentUtil.translationWithColor(TextFormatting.DARK_RED, - "gregtech.machine.machine_hatch.locked")); - } + builder.setWorkingStatus(recipeMapWorkable.isWorkingEnabled(), recipeMapWorkable.isActive()) + .addEnergyUsageLine(this.getEnergyContainer()) + .addEnergyTierLine(GTUtility.getTierByVoltage(recipeMapWorkable.getMaxVoltage())) + .addCustom((manager, syncer) -> { + if (!isStructureFormed()) return; + + // Machine mode text + // Shared text components for both states + IKey maxMachinesText = KeyUtil.number(TextFormatting.DARK_PURPLE, + syncer.syncInt(getMachineLimit())); + maxMachinesText = KeyUtil.lang(TextFormatting.GRAY, + "gregtech.machine.machine_hatch.machines_max", maxMachinesText); + + if (syncer.syncBoolean(logic.activeRecipeMap == null)) { + // No machines in hatch + IKey noneText = KeyUtil.lang(TextFormatting.YELLOW, + "gregtech.machine.machine_hatch.machines_none"); + IKey bodyText = KeyUtil.lang(TextFormatting.GRAY, + "gregtech.machine.machine_hatch.machines", noneText); + IKey hoverText1 = KeyUtil.lang(TextFormatting.GRAY, + "gregtech.machine.machine_hatch.machines_none_hover"); + manager.add(KeyUtil.setHover(bodyText, hoverText1, maxMachinesText)); + } else { + // Some amount of machines in hatch + String key = syncer.syncString(logic.getMachineStack().getTranslationKey()); + IKey mapText = KeyUtil.lang(TextFormatting.DARK_PURPLE, + key + ".name"); + mapText = KeyUtil.string( + TextFormatting.DARK_PURPLE, + "%sx %s", + syncer.syncInt(logic.getParallelLimit()), mapText); + IKey bodyText = KeyUtil.lang(TextFormatting.GRAY, + "gregtech.machine.machine_hatch.machines", mapText); + int tier = syncer.syncInt(logic.machineTier); + IKey voltageName = KeyUtil.string(GTValues.VNF[tier]); + int amps = syncer.syncInt(logic.getMachineStack().getCount()); + String energyFormatted = TextFormattingUtil + .formatNumbers(GTValues.V[tier] * amps); + IKey hoverText = KeyUtil.lang( + TextFormatting.GRAY, + "gregtech.machine.machine_hatch.machines_max_eut", + energyFormatted, amps, voltageName); + manager.add(KeyUtil.setHover(bodyText, hoverText, maxMachinesText)); + } + + // Hatch locked status + if (syncer.syncBoolean(isActive())) { + manager.add(KeyUtil.lang(TextFormatting.DARK_RED, + "gregtech.machine.machine_hatch.locked")); } }) + .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 b4715563582..5e225eca35e 100644 --- a/src/main/java/gregtech/common/metatileentities/multi/electric/MetaTileEntityPyrolyseOven.java +++ b/src/main/java/gregtech/common/metatileentities/multi/electric/MetaTileEntityPyrolyseOven.java @@ -5,8 +5,8 @@ import gregtech.api.metatileentity.MetaTileEntity; import gregtech.api.metatileentity.interfaces.IGregTechTileEntity; import gregtech.api.metatileentity.multiblock.IMultiblockPart; -import gregtech.api.metatileentity.multiblock.MultiblockDisplayText; import gregtech.api.metatileentity.multiblock.RecipeMapMultiblockController; +import gregtech.api.metatileentity.multiblock.ui.MultiblockUIBuilder; import gregtech.api.pattern.BlockPattern; import gregtech.api.pattern.FactoryBlockPattern; import gregtech.api.pattern.PatternMatchContext; @@ -14,7 +14,7 @@ import gregtech.api.recipes.logic.OCResult; import gregtech.api.recipes.properties.RecipePropertyStorage; import gregtech.api.util.GTUtility; -import gregtech.api.util.TextComponentUtil; +import gregtech.api.util.KeyUtil; import gregtech.client.renderer.ICubeRenderer; import gregtech.client.renderer.texture.Textures; import gregtech.common.blocks.BlockMachineCasing.MachineCasingType; @@ -26,12 +26,12 @@ import net.minecraft.item.ItemStack; import net.minecraft.util.ResourceLocation; import net.minecraft.util.SoundEvent; -import net.minecraft.util.text.ITextComponent; import net.minecraft.util.text.TextFormatting; import net.minecraft.world.World; import net.minecraftforge.fml.relauncher.Side; import net.minecraftforge.fml.relauncher.SideOnly; +import com.cleanroommc.modularui.api.drawable.IKey; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; @@ -97,36 +97,6 @@ protected void formStructure(PatternMatchContext context) { this.coilTier = 0; } - @Override - protected void addDisplayText(List textList) { - MultiblockDisplayText.builder(textList, isStructureFormed()) - .setWorkingStatus(recipeMapWorkable.isWorkingEnabled(), recipeMapWorkable.isActive()) - .addEnergyUsageLine(recipeMapWorkable.getEnergyContainer()) - .addEnergyTierLine(GTUtility.getTierByVoltage(recipeMapWorkable.getMaxVoltage())) - .addCustom(tl -> { - if (isStructureFormed()) { - int processingSpeed = coilTier == 0 ? 75 : 50 * (coilTier + 1); - ITextComponent speedIncrease = TextComponentUtil.stringWithColor( - getSpeedColor(processingSpeed), - processingSpeed + "%"); - - ITextComponent base = TextComponentUtil.translationWithColor( - TextFormatting.GRAY, - "gregtech.multiblock.pyrolyse_oven.speed", - speedIncrease); - - ITextComponent hover = TextComponentUtil.translationWithColor( - TextFormatting.GRAY, - "gregtech.multiblock.pyrolyse_oven.speed_hover"); - - tl.add(TextComponentUtil.setHover(base, hover)); - } - }) - .addParallelsLine(recipeMapWorkable.getParallelLimit()) - .addWorkingStatusLine() - .addProgressLine(recipeMapWorkable.getProgressPercent()); - } - private TextFormatting getSpeedColor(int speed) { if (speed < 100) { return TextFormatting.RED; @@ -139,6 +109,30 @@ private TextFormatting getSpeedColor(int speed) { } } + @Override + protected void configureDisplayText(MultiblockUIBuilder builder) { + builder.setWorkingStatus(recipeMapWorkable.isWorkingEnabled(), recipeMapWorkable.isActive()) + .addEnergyUsageLine(this.getEnergyContainer()) + .addEnergyTierLine(GTUtility.getTierByVoltage(recipeMapWorkable.getMaxVoltage())) + .addCustom((textList, syncer) -> { + if (!isStructureFormed()) return; + int tier = syncer.syncInt(coilTier); + + int processingSpeed = tier == 0 ? 75 : 50 * (tier + 1); + IKey speed = KeyUtil.number(() -> getSpeedColor(processingSpeed), processingSpeed, "%"); + + IKey body = KeyUtil.lang(TextFormatting.GRAY, + "gregtech.multiblock.pyrolyse_oven.speed", speed); + IKey hover = KeyUtil.lang(TextFormatting.GRAY, + "gregtech.multiblock.pyrolyse_oven.speed_hover"); + textList.add(KeyUtil.setHover(body, hover)); + }) + .addParallelsLine(recipeMapWorkable.getParallelLimit()) + .addWorkingStatusLine() + .addProgressLine(recipeMapWorkable.getProgress(), recipeMapWorkable.getMaxProgress()) + .addRecipeOutputLine(recipeMapWorkable); + } + @Override public void addInformation(ItemStack stack, @Nullable World player, List tooltip, boolean advanced) { super.addInformation(stack, player, tooltip, advanced); 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 50923590e11..4f1810fefc4 100644 --- a/src/main/java/gregtech/common/metatileentities/multi/electric/MetaTileEntityResearchStation.java +++ b/src/main/java/gregtech/common/metatileentities/multi/electric/MetaTileEntityResearchStation.java @@ -11,20 +11,25 @@ import gregtech.api.metatileentity.interfaces.IGregTechTileEntity; import gregtech.api.metatileentity.multiblock.IMultiblockPart; import gregtech.api.metatileentity.multiblock.MultiblockAbility; -import gregtech.api.metatileentity.multiblock.MultiblockDisplayText; import gregtech.api.metatileentity.multiblock.RecipeMapMultiblockController; +import gregtech.api.metatileentity.multiblock.ui.KeyManager; +import gregtech.api.metatileentity.multiblock.ui.MultiblockUIBuilder; +import gregtech.api.metatileentity.multiblock.ui.UISyncer; import gregtech.api.pattern.BlockPattern; import gregtech.api.pattern.FactoryBlockPattern; import gregtech.api.pattern.MultiblockShapeInfo; import gregtech.api.pattern.PatternMatchContext; import gregtech.api.recipes.Recipe; import gregtech.api.recipes.RecipeMaps; +import gregtech.api.util.AssemblyLineManager; import gregtech.api.util.GTUtility; +import gregtech.api.util.KeyUtil; import gregtech.client.renderer.ICubeRenderer; import gregtech.client.renderer.texture.Textures; import gregtech.common.ConfigHolder; import gregtech.common.blocks.BlockComputerCasing; import gregtech.common.blocks.MetaBlocks; +import gregtech.common.items.behaviors.DataItemBehavior; import gregtech.common.metatileentities.MetaTileEntities; import net.minecraft.block.state.IBlockState; @@ -33,15 +38,17 @@ import net.minecraft.item.ItemStack; import net.minecraft.util.EnumFacing; import net.minecraft.util.ResourceLocation; -import net.minecraft.util.text.ITextComponent; +import net.minecraft.util.text.TextFormatting; import net.minecraft.world.World; import net.minecraftforge.fml.relauncher.Side; import net.minecraftforge.fml.relauncher.SideOnly; import net.minecraftforge.items.IItemHandlerModifiable; +import com.cleanroommc.modularui.utils.serialization.ByteBufAdapters; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; +import java.util.ArrayList; import java.util.Collections; import java.util.List; @@ -201,7 +208,7 @@ protected ICubeRenderer getFrontOverlay() { } @Override - protected boolean shouldShowVoidingModeButton() { + public boolean shouldShowVoidingModeButton() { return false; } @@ -223,27 +230,50 @@ public void addInformation(ItemStack stack, @Nullable World world, @NotNull List } @Override - protected void addDisplayText(List textList) { - MultiblockDisplayText.builder(textList, isStructureFormed()) - .setWorkingStatus(recipeMapWorkable.isWorkingEnabled(), recipeMapWorkable.isActive()) - .setWorkingStatusKeys( - "gregtech.multiblock.idling", - "gregtech.multiblock.work_paused", - "gregtech.machine.research_station.researching") - .addEnergyUsageLine(recipeMapWorkable.getEnergyContainer()) + protected void configureDisplayText(MultiblockUIBuilder builder) { + builder.setWorkingStatus(recipeMapWorkable.isWorkingEnabled(), recipeMapWorkable.isActive()) + .addEnergyUsageLine(this.getEnergyContainer()) .addEnergyTierLine(GTUtility.getTierByVoltage(recipeMapWorkable.getMaxVoltage())) .addComputationUsageExactLine(getRecipeMapWorkable().getCurrentDrawnCWUt()) - .addParallelsLine(recipeMapWorkable.getParallelLimit()) - .addWorkingStatusLine() - .addProgressLine(recipeMapWorkable.getProgressPercent()); + .addParallelsLine(recipeMapWorkable.getParallelLimit()); + + if (!recipeMapWorkable.isWorkingEnabled()) + builder.addWorkPausedLine(false); + else if (recipeMapWorkable.isWorking()) { + builder.addCustom(this::researchingLine); + } else { + builder.addIdlingLine(false); + } + + builder.addComputationProgressLine(getRecipeMapWorkable()); } @Override - protected void addWarningText(List textList) { - MultiblockDisplayText.builder(textList, isStructureFormed(), false) - .addLowPowerLine(recipeMapWorkable.isHasNotEnoughEnergy()) - .addLowComputationLine(getRecipeMapWorkable().isHasNotEnoughComputation()) - .addMaintenanceProblemLines(getMaintenanceProblems()); + protected void configureWarningText(MultiblockUIBuilder builder) { + builder.addLowComputationLine(getRecipeMapWorkable().isHasNotEnoughComputation()); + super.configureWarningText(builder); + } + + private void researchingLine(KeyManager manager, UISyncer syncer) { + var recipe = getRecipeMapWorkable().getPreviousRecipe(); + // todo fix recipe null on world load at some future point + if (syncer.syncBoolean(recipe == null)) return; + ItemStack stack = ItemStack.EMPTY; + if (recipe != null) { + List outputs = recipe.getOutputs(); + stack = outputs.get(outputs.size() - 1); + } + stack = syncer.syncObject(stack, ByteBufAdapters.ITEM_STACK); + if (stack.isEmpty()) return; + String id = AssemblyLineManager.readResearchId(stack); + if (id == null) return; + List stacks = new ArrayList<>(); + DataItemBehavior.collectResearchItems(id, stacks); + stacks.remove(0); + manager.add(KeyUtil.lang(TextFormatting.GREEN, "gregtech.machine.research_station.researching")); + for (String line : stacks) { + manager.add(KeyUtil.string(line)); + } } private static class ResearchStationRecipeLogic extends ComputationRecipeLogic { diff --git a/src/main/java/gregtech/common/metatileentities/multi/electric/centralmonitor/MetaTileEntityCentralMonitor.java b/src/main/java/gregtech/common/metatileentities/multi/electric/centralmonitor/MetaTileEntityCentralMonitor.java index 4deeabb9a9f..222907d0e08 100644 --- a/src/main/java/gregtech/common/metatileentities/multi/electric/centralmonitor/MetaTileEntityCentralMonitor.java +++ b/src/main/java/gregtech/common/metatileentities/multi/electric/centralmonitor/MetaTileEntityCentralMonitor.java @@ -7,8 +7,6 @@ import gregtech.api.cover.Cover; import gregtech.api.gui.GuiTextures; import gregtech.api.gui.ModularUI; -import gregtech.api.gui.Widget; -import gregtech.api.gui.widgets.AdvancedTextWidget; import gregtech.api.gui.widgets.WidgetGroup; import gregtech.api.metatileentity.IFastRenderMetaTileEntity; import gregtech.api.metatileentity.MetaTileEntity; @@ -16,12 +14,15 @@ import gregtech.api.metatileentity.multiblock.IMultiblockPart; import gregtech.api.metatileentity.multiblock.MultiblockAbility; import gregtech.api.metatileentity.multiblock.MultiblockWithDisplayBase; +import gregtech.api.metatileentity.multiblock.ui.MultiblockUIBuilder; +import gregtech.api.metatileentity.multiblock.ui.MultiblockUIFactory; import gregtech.api.pattern.BlockPattern; import gregtech.api.pattern.FactoryBlockPattern; import gregtech.api.pattern.PatternMatchContext; import gregtech.api.pipenet.tile.IPipeTile; import gregtech.api.pipenet.tile.TileEntityPipeBase; import gregtech.api.util.FacingPos; +import gregtech.api.util.KeyUtil; import gregtech.client.renderer.ICubeRenderer; import gregtech.client.renderer.texture.Textures; import gregtech.client.utils.RenderUtil; @@ -49,9 +50,7 @@ import net.minecraft.util.math.AxisAlignedBB; import net.minecraft.util.math.BlockPos; import net.minecraft.util.math.RayTraceResult; -import net.minecraft.util.text.ITextComponent; -import net.minecraft.util.text.TextComponentString; -import net.minecraft.util.text.TextComponentTranslation; +import net.minecraft.util.text.TextFormatting; import net.minecraft.world.World; import net.minecraftforge.common.capabilities.Capability; import net.minecraftforge.fml.relauncher.Side; @@ -60,6 +59,9 @@ import codechicken.lib.render.CCRenderState; import codechicken.lib.render.pipeline.IVertexOperation; import codechicken.lib.vec.Matrix4; +import com.cleanroommc.modularui.api.drawable.IKey; +import com.cleanroommc.modularui.value.sync.IntSyncValue; +import com.cleanroommc.modularui.widgets.ButtonWidget; import org.jetbrains.annotations.Nullable; import org.lwjgl.opengl.GL11; @@ -70,7 +72,7 @@ public class MetaTileEntityCentralMonitor extends MultiblockWithDisplayBase implements IFastRenderMetaTileEntity { - private final static long ENERGY_COST = -ConfigHolder.machines.centralMonitorEuCost; + private final static long ENERGY_COST = ConfigHolder.machines.centralMonitorEuCost; public final static int MAX_HEIGHT = 9; public final static int MAX_WIDTH = 14; // run-time data @@ -278,33 +280,58 @@ public void renderMetaTileEntity(CCRenderState renderState, Matrix4 translation, } @Override - protected void addDisplayText(List textList) { - super.addDisplayText(textList); - textList.add(new TextComponentTranslation("gregtech.multiblock.central_monitor.height", this.height)); - if (!isStructureFormed()) { - ITextComponent buttonText = new TextComponentTranslation( - "gregtech.multiblock.central_monitor.height_modify", height); - buttonText.appendText(" "); - buttonText.appendSibling(AdvancedTextWidget.withButton(new TextComponentString("[-]"), "sub")); - buttonText.appendText(" "); - buttonText.appendSibling(AdvancedTextWidget.withButton(new TextComponentString("[+]"), "add")); - textList.add(buttonText); - } else { - textList.add(new TextComponentTranslation("gregtech.multiblock.central_monitor.width", this.width)); - textList.add(new TextComponentTranslation("gregtech.multiblock.central_monitor.low_power")); - } + protected MultiblockUIFactory createUIFactory() { + return super.createUIFactory() + .createFlexButton((posGuiData, panelSyncManager) -> { + IntSyncValue intSync = new IntSyncValue(() -> height, this::setHeight); + panelSyncManager.syncValue("height", intSync); + + // todo make this a popup? + return new ButtonWidget<>() + .addTooltipLine(IKey.lang("gregtech.multiblock.central_monitor.button_tooltip")) + .onMousePressed(mouseData -> { + int currentHeight = intSync.getIntValue(); + + if (mouseData == 0 && currentHeight < MAX_HEIGHT) { + intSync.setIntValue(currentHeight + 1); + return true; + } else if (mouseData == 1 && currentHeight > 3) { + intSync.setIntValue(currentHeight - 1); + return true; + } else if (mouseData == 2) { + intSync.setIntValue(3); + } + + return false; + }); + }); } @Override - protected boolean shouldShowVoidingModeButton() { - return false; + protected void configureDisplayText(MultiblockUIBuilder builder) { + builder.addCustom((list, syncer) -> { + list.add(KeyUtil.lang(TextFormatting.GRAY, "gregtech.multiblock.central_monitor.height", + syncer.syncInt(this.height))); + + if (isStructureFormed()) { + list.add(KeyUtil.lang(TextFormatting.GRAY, "gregtech.multiblock.central_monitor.width", + syncer.syncInt(this.width))); + } + }); + } + + @Override + protected void configureWarningText(MultiblockUIBuilder builder) { + builder.addCustom((list, syncer) -> { + if (isStructureFormed() && syncer.syncBoolean(() -> !drainEnergy(true))) { + list.add(KeyUtil.lang(TextFormatting.GRAY, "gregtech.multiblock.central_monitor.low_power")); + } + }); } @Override - protected void handleDisplayClick(String componentData, Widget.ClickData clickData) { - super.handleDisplayClick(componentData, clickData); - int modifier = componentData.equals("add") ? 1 : -1; - setHeight(this.height + modifier); + public boolean shouldShowVoidingModeButton() { + return false; } @Override @@ -370,8 +397,7 @@ public MetaTileEntity createMetaTileEntity(IGregTechTileEntity metaTileEntityHol @Override protected void updateFormedValid() { if (this.getOffsetTimer() % 20 == 0) { - setActive(inputEnergy.changeEnergy(ENERGY_COST * this.getMultiblockParts().size()) == - ENERGY_COST * this.getMultiblockParts().size()); + setActive(drainEnergy(false)); if (checkCovers()) { this.getMultiblockParts().forEach(part -> { Set covers = getAllCovers(); @@ -384,6 +410,20 @@ protected void updateFormedValid() { } } + /** + * @return if the Central Monitor was able to drain enough energy. + */ + private boolean drainEnergy(boolean simulate) { + long energyToDrain = ENERGY_COST * this.getMultiblockParts().size(); + long resultEnergy = inputEnergy.getEnergyStored() - energyToDrain; + if (resultEnergy >= 0L && resultEnergy <= inputEnergy.getEnergyCapacity()) { + if (!simulate) + inputEnergy.changeEnergy(-energyToDrain); + return true; + } + return false; + } + public Set getAllCovers() { Set allCovers = new HashSet<>(); if (netCovers != null) { @@ -630,7 +670,7 @@ public void addInformation(ItemStack stack, @Nullable World player, List tooltip.add(I18n.format("gregtech.multiblock.central_monitor.tooltip.1")); tooltip.add(I18n.format("gregtech.multiblock.central_monitor.tooltip.2", MAX_WIDTH, MAX_HEIGHT)); tooltip.add(I18n.format("gregtech.multiblock.central_monitor.tooltip.3")); - tooltip.add(I18n.format("gregtech.multiblock.central_monitor.tooltip.4", -ENERGY_COST)); + tooltip.add(I18n.format("gregtech.multiblock.central_monitor.tooltip.4", ENERGY_COST)); } @Override diff --git a/src/main/java/gregtech/common/metatileentities/multi/electric/generator/MetaTileEntityLargeCombustionEngine.java b/src/main/java/gregtech/common/metatileentities/multi/electric/generator/MetaTileEntityLargeCombustionEngine.java index 5bdd31eafc8..7c367d349b6 100644 --- a/src/main/java/gregtech/common/metatileentities/multi/electric/generator/MetaTileEntityLargeCombustionEngine.java +++ b/src/main/java/gregtech/common/metatileentities/multi/electric/generator/MetaTileEntityLargeCombustionEngine.java @@ -6,24 +6,24 @@ import gregtech.api.capability.IMultipleTankHandler; import gregtech.api.capability.impl.MultiblockFuelRecipeLogic; import gregtech.api.fluids.store.FluidStorageKeys; -import gregtech.api.gui.GuiTextures; -import gregtech.api.gui.resources.TextureArea; import gregtech.api.metatileentity.MetaTileEntity; import gregtech.api.metatileentity.interfaces.IGregTechTileEntity; +import gregtech.api.metatileentity.multiblock.*; import gregtech.api.metatileentity.multiblock.FuelMultiblockController; import gregtech.api.metatileentity.multiblock.IMultiblockPart; -import gregtech.api.metatileentity.multiblock.IProgressBarMultiblock; import gregtech.api.metatileentity.multiblock.MultiblockAbility; -import gregtech.api.metatileentity.multiblock.MultiblockDisplayText; import gregtech.api.metatileentity.multiblock.RecipeMapMultiblockController; +import gregtech.api.metatileentity.multiblock.ui.MultiblockUIBuilder; +import gregtech.api.metatileentity.multiblock.ui.TemplateBarBuilder; +import gregtech.api.mui.GTGuiTextures; +import gregtech.api.mui.sync.FixedIntArraySyncValue; import gregtech.api.pattern.BlockPattern; import gregtech.api.pattern.FactoryBlockPattern; import gregtech.api.pattern.PatternMatchContext; import gregtech.api.recipes.RecipeMaps; import gregtech.api.unification.material.Materials; +import gregtech.api.util.KeyUtil; import gregtech.api.util.RelativeDirection; -import gregtech.api.util.TextComponentUtil; -import gregtech.api.util.TextFormattingUtil; import gregtech.client.renderer.ICubeRenderer; import gregtech.client.renderer.texture.Textures; import gregtech.common.blocks.BlockMetalCasing.MetalCasingType; @@ -36,23 +36,29 @@ import net.minecraft.item.ItemStack; import net.minecraft.util.ResourceLocation; import net.minecraft.util.math.BlockPos; -import net.minecraft.util.text.ITextComponent; import net.minecraft.util.text.TextFormatting; import net.minecraft.world.World; +import net.minecraftforge.fluids.Fluid; import net.minecraftforge.fluids.FluidStack; import net.minecraftforge.fml.relauncher.Side; import net.minecraftforge.fml.relauncher.SideOnly; +import com.cleanroommc.modularui.api.drawable.IKey; +import com.cleanroommc.modularui.value.sync.BooleanSyncValue; +import com.cleanroommc.modularui.value.sync.PanelSyncManager; +import com.cleanroommc.modularui.value.sync.StringSyncValue; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import java.util.List; +import java.util.function.UnaryOperator; -public class MetaTileEntityLargeCombustionEngine extends FuelMultiblockController implements IProgressBarMultiblock { +public class MetaTileEntityLargeCombustionEngine extends FuelMultiblockController implements ProgressBarMultiblock { private final int tier; private final boolean isExtreme; private boolean boostAllowed; + private boolean hasLubricant; public MetaTileEntityLargeCombustionEngine(ResourceLocation metaTileEntityId, int tier) { super(metaTileEntityId, RecipeMaps.COMBUSTION_GENERATOR_FUELS, tier); @@ -68,11 +74,10 @@ public MetaTileEntity createMetaTileEntity(IGregTechTileEntity tileEntity) { } @Override - protected void addDisplayText(List textList) { - LargeCombustionEngineWorkableHandler recipeLogic = ((LargeCombustionEngineWorkableHandler) recipeMapWorkable); + protected void configureDisplayText(MultiblockUIBuilder builder) { + var recipeLogic = (LargeCombustionEngineWorkableHandler) recipeMapWorkable; - MultiblockDisplayText.Builder builder = MultiblockDisplayText.builder(textList, isStructureFormed()) - .setWorkingStatus(recipeLogic.isWorkingEnabled(), recipeLogic.isActive()); + builder.setWorkingStatus(recipeLogic.isWorkingEnabled(), recipeLogic.isActive() && !isDynamoFull()); if (isExtreme) { builder.addEnergyProductionLine(GTValues.V[tier + 1], recipeLogic.getRecipeEUt()); @@ -81,34 +86,48 @@ protected void addDisplayText(List textList) { } builder.addFuelNeededLine(recipeLogic.getRecipeFluidInputInfo(), recipeLogic.getPreviousRecipeDuration()) - .addCustom(tl -> { - if (isStructureFormed() && recipeLogic.isOxygenBoosted) { - String key = isExtreme ? "gregtech.multiblock.large_combustion_engine.liquid_oxygen_boosted" : + .addCustom((richText, syncer) -> { + if (isStructureFormed() && syncer.syncBoolean(recipeLogic.isOxygenBoosted)) { + String key = isExtreme ? + "gregtech.multiblock.large_combustion_engine.liquid_oxygen_boosted" : "gregtech.multiblock.large_combustion_engine.oxygen_boosted"; - tl.add(TextComponentUtil.translationWithColor(TextFormatting.AQUA, key)); + richText.add(KeyUtil.lang(TextFormatting.AQUA, key)); } }) .addWorkingStatusLine(); } @Override - protected void addErrorText(List textList) { - super.addErrorText(textList); - if (isStructureFormed()) { - if (checkIntakesObstructed()) { - textList.add(TextComponentUtil.translationWithColor(TextFormatting.RED, + protected void configureErrorText(MultiblockUIBuilder builder) { + super.configureErrorText(builder); + var recipeLogic = (LargeCombustionEngineWorkableHandler) recipeMapWorkable; + + builder.addCustom((keyList, syncer) -> { + if (!isStructureFormed()) return; + + if (syncer.syncBoolean(checkIntakesObstructed())) { + keyList.add(KeyUtil.lang(TextFormatting.RED, "gregtech.multiblock.large_combustion_engine.obstructed")); - textList.add(TextComponentUtil.translationWithColor(TextFormatting.GRAY, + keyList.add(KeyUtil.lang(TextFormatting.GRAY, "gregtech.multiblock.large_combustion_engine.obstructed.desc")); } - FluidStack lubricantStack = getInputFluidInventory().drain(Materials.Lubricant.getFluid(Integer.MAX_VALUE), - false); - if (lubricantStack == null || lubricantStack.amount == 0) { - textList.add(TextComponentUtil.translationWithColor(TextFormatting.RED, + if (syncer.syncBoolean(!recipeLogic.checkLubricant())) { + keyList.add(KeyUtil.lang(TextFormatting.RED, "gregtech.multiblock.large_combustion_engine.no_lubricant")); } - } + }); + } + + @Override + protected void configureWarningText(MultiblockUIBuilder builder) { + super.configureWarningText(builder); + builder.addCustom((manager, syncer) -> { + if (syncer.syncBoolean(this::isDynamoFull)) { + manager.add(KeyUtil.lang(TextFormatting.YELLOW, + "gregtech.multiblock.large_combustion_engine.dynamo_hatch_full")); + } + }); } @Override @@ -126,7 +145,7 @@ public void addInformation(ItemStack stack, @Nullable World player, List } @Override - protected BlockPattern createStructurePattern() { + protected @NotNull BlockPattern createStructurePattern() { return FactoryBlockPattern.start() .aisle("XXX", "XDX", "XXX") .aisle("XCX", "CGC", "XCX") @@ -216,7 +235,7 @@ private boolean checkIntakesObstructed() { } @Override - protected boolean shouldShowVoidingModeButton() { + public boolean shouldShowVoidingModeButton() { return false; } @@ -225,107 +244,129 @@ public boolean isBoostAllowed() { } @Override - public int getNumProgressBars() { + public int getProgressBarCount() { return 3; } @Override - public double getFillPercentage(int index) { - if (index == 0) { - int[] fuelAmount = new int[2]; - if (getInputFluidInventory() != null) { - MultiblockFuelRecipeLogic recipeLogic = (MultiblockFuelRecipeLogic) recipeMapWorkable; - if (recipeLogic.getInputFluidStack() != null) { - FluidStack testStack = recipeLogic.getInputFluidStack().copy(); - testStack.amount = Integer.MAX_VALUE; - fuelAmount = getTotalFluidAmount(testStack, getInputFluidInventory()); - } + public void registerBars(List> bars, PanelSyncManager syncManager) { + FixedIntArraySyncValue fuelValue = new FixedIntArraySyncValue(this::getFuelAmount, null); + syncManager.syncValue("fuel_amount", fuelValue); + StringSyncValue fuelNameValue = new StringSyncValue(() -> { + FluidStack stack = ((MultiblockFuelRecipeLogic) recipeMapWorkable).getInputFluidStack(); + if (stack == null) { + return null; } - return fuelAmount[1] != 0 ? 1.0 * fuelAmount[0] / fuelAmount[1] : 0; - } else if (index == 1) { - int[] lubricantAmount = new int[2]; - if (getInputFluidInventory() != null) { - lubricantAmount = getTotalFluidAmount(Materials.Lubricant.getFluid(Integer.MAX_VALUE), - getInputFluidInventory()); + Fluid fluid = stack.getFluid(); + if (fluid == null) { + return null; } - return lubricantAmount[1] != 0 ? 1.0 * lubricantAmount[0] / lubricantAmount[1] : 0; - } else { - int[] oxygenAmount = new int[2]; - if (getInputFluidInventory() != null) { - if (isBoostAllowed()) { - FluidStack oxygenStack = isExtreme ? - Materials.Oxygen.getFluid(FluidStorageKeys.LIQUID, Integer.MAX_VALUE) : - Materials.Oxygen.getFluid(Integer.MAX_VALUE); - oxygenAmount = getTotalFluidAmount(oxygenStack, getInputFluidInventory()); - } + return fluid.getName(); + }); + syncManager.syncValue("fuel_name", fuelNameValue); + FixedIntArraySyncValue lubricantValue = new FixedIntArraySyncValue(this::getLubricantAmount, null); + syncManager.syncValue("lubricant_amount", lubricantValue); + FixedIntArraySyncValue oxygenValue = new FixedIntArraySyncValue(this::getOxygenAmount, null); + syncManager.syncValue("oxygen_amount", oxygenValue); + BooleanSyncValue boostValue = new BooleanSyncValue(this::isBoostAllowed); + syncManager.syncValue("boost_allowed", boostValue); + + bars.add(barTest -> barTest + .progress(() -> fuelValue.getValue(1) == 0 ? 0 : + 1.0 * fuelValue.getValue(0) / fuelValue.getValue(1)) + .texture(GTGuiTextures.PROGRESS_BAR_LCE_FUEL) + .tooltipBuilder(t -> createFuelTooltip(t, fuelValue, fuelNameValue))); + + bars.add(barTest -> barTest + .progress(() -> lubricantValue.getValue(1) == 0 ? 0 : + 1.0 * lubricantValue.getValue(0) / lubricantValue.getValue(1)) + .texture(GTGuiTextures.PROGRESS_BAR_LCE_LUBRICANT) + .tooltipBuilder(t -> { + if (isStructureFormed()) { + if (lubricantValue.getValue(0) == 0) { + t.addLine(IKey.lang("gregtech.multiblock.large_combustion_engine.no_lubricant")); + } else { + t.addLine(IKey.lang("gregtech.multiblock.large_combustion_engine.lubricant_amount", + lubricantValue.getValue(0), lubricantValue.getValue(1))); + } + } else { + t.addLine(IKey.lang("gregtech.multiblock.invalid_structure")); + } + })); + + bars.add(barTest -> barTest + .progress(() -> oxygenValue.getValue(1) == 0 ? 0 : + 1.0 * oxygenValue.getValue(0) / oxygenValue.getValue(1)) + .texture(GTGuiTextures.PROGRESS_BAR_LCE_OXYGEN) + .tooltipBuilder(t -> { + if (isStructureFormed()) { + if (boostValue.getBoolValue()) { + if (oxygenValue.getValue(0) == 0) { + t.addLine(IKey.lang("gregtech.multiblock.large_combustion_engine.oxygen_none")); + } else if (isExtreme) { + t.addLine(IKey.lang( + "gregtech.multiblock.large_combustion_engine.liquid_oxygen_amount", + oxygenValue.getValue(0), oxygenValue.getValue(1))); + } else { + t.addLine(IKey.lang("gregtech.multiblock.large_combustion_engine.oxygen_amount", + oxygenValue.getValue(0), oxygenValue.getValue(1))); + } + } else if (isExtreme) { + t.addLine(IKey.lang( + "gregtech.multiblock.large_combustion_engine.liquid_oxygen_boost_disallowed")); + } else { + t.addLine(IKey.lang( + "gregtech.multiblock.large_combustion_engine.oxygen_boost_disallowed")); + } + } else { + t.addLine(IKey.lang("gregtech.multiblock.invalid_structure")); + } + })); + } + + /** + * @return an array of [fuel stored, fuel capacity] + */ + private int[] getFuelAmount() { + if (getInputFluidInventory() != null) { + MultiblockFuelRecipeLogic recipeLogic = (MultiblockFuelRecipeLogic) recipeMapWorkable; + if (recipeLogic.getInputFluidStack() != null) { + FluidStack testStack = recipeLogic.getInputFluidStack().copy(); + testStack.amount = Integer.MAX_VALUE; + return getTotalFluidAmount(testStack, getInputFluidInventory()); } - return oxygenAmount[1] != 0 ? 1.0 * oxygenAmount[0] / oxygenAmount[1] : 0; } + return new int[2]; } - @Override - public TextureArea getProgressBarTexture(int index) { - if (index == 0) { - return GuiTextures.PROGRESS_BAR_LCE_FUEL; - } else if (index == 1) { - return GuiTextures.PROGRESS_BAR_LCE_LUBRICANT; - } else { - return GuiTextures.PROGRESS_BAR_LCE_OXYGEN; + /** + * @return an array of [lubricant stored, lubricant capacity] + */ + private int[] getLubricantAmount() { + if (getInputFluidInventory() != null) { + return getTotalFluidAmount(Materials.Lubricant.getFluid(Integer.MAX_VALUE), + getInputFluidInventory()); } + return new int[2]; } - @Override - public void addBarHoverText(List hoverList, int index) { - if (index == 0) { - addFuelText(hoverList); - } else if (index == 1) { - // Lubricant - int lubricantStored = 0; - int lubricantCapacity = 0; - if (isStructureFormed() && getInputFluidInventory() != null) { - // Hunt for tanks with lubricant in them - int[] lubricantAmount = getTotalFluidAmount(Materials.Lubricant.getFluid(Integer.MAX_VALUE), - getInputFluidInventory()); - lubricantStored = lubricantAmount[0]; - lubricantCapacity = lubricantAmount[1]; - } - - ITextComponent lubricantInfo = TextComponentUtil.stringWithColor( - TextFormatting.GOLD, - TextFormattingUtil.formatNumbers(lubricantStored) + " / " + - TextFormattingUtil.formatNumbers(lubricantCapacity) + " L"); - hoverList.add(TextComponentUtil.translationWithColor( - TextFormatting.GRAY, - "gregtech.multiblock.large_combustion_engine.lubricant_amount", - lubricantInfo)); - } else { - // Oxygen/LOX + /** + * @return an array of [oxygen stored, oxygen capacity] + */ + private int[] getOxygenAmount() { + if (getInputFluidInventory() != null) { if (isBoostAllowed()) { - int oxygenStored = 0; - int oxygenCapacity = 0; - if (isStructureFormed() && getInputFluidInventory() != null) { - // Hunt for tanks with Oxygen or LOX (depending on tier) in them - FluidStack oxygenStack = isExtreme ? - Materials.Oxygen.getFluid(FluidStorageKeys.LIQUID, Integer.MAX_VALUE) : - Materials.Oxygen.getFluid(Integer.MAX_VALUE); - int[] oxygenAmount = getTotalFluidAmount(oxygenStack, getInputFluidInventory()); - oxygenStored = oxygenAmount[0]; - oxygenCapacity = oxygenAmount[1]; - } - - ITextComponent oxygenInfo = TextComponentUtil.stringWithColor( - TextFormatting.AQUA, - TextFormattingUtil.formatNumbers(oxygenStored) + " / " + - TextFormattingUtil.formatNumbers(oxygenCapacity) + " L"); - String key = isExtreme ? "gregtech.multiblock.large_combustion_engine.liquid_oxygen_amount" : - "gregtech.multiblock.large_combustion_engine.oxygen_amount"; - hoverList.add(TextComponentUtil.translationWithColor(TextFormatting.GRAY, key, oxygenInfo)); - } else { - String key = isExtreme ? "gregtech.multiblock.large_combustion_engine.liquid_oxygen_boost_disallowed" : - "gregtech.multiblock.large_combustion_engine.oxygen_boost_disallowed"; - hoverList.add(TextComponentUtil.translationWithColor(TextFormatting.YELLOW, key)); + FluidStack oxygenStack = isExtreme ? + Materials.Oxygen.getFluid(FluidStorageKeys.LIQUID, Integer.MAX_VALUE) : + Materials.Oxygen.getFluid(Integer.MAX_VALUE); + return getTotalFluidAmount(oxygenStack, getInputFluidInventory()); } } + return new int[2]; + } + + public boolean isDynamoFull() { + return getEnergyContainer().getEnergyCanBeInserted() < recipeMapWorkable.getRecipeEUt(); } private static class LargeCombustionEngineWorkableHandler extends MultiblockFuelRecipeLogic { diff --git a/src/main/java/gregtech/common/metatileentities/multi/electric/generator/MetaTileEntityLargeTurbine.java b/src/main/java/gregtech/common/metatileentities/multi/electric/generator/MetaTileEntityLargeTurbine.java index 16522799167..5a15302484d 100644 --- a/src/main/java/gregtech/common/metatileentities/multi/electric/generator/MetaTileEntityLargeTurbine.java +++ b/src/main/java/gregtech/common/metatileentities/multi/electric/generator/MetaTileEntityLargeTurbine.java @@ -4,39 +4,45 @@ import gregtech.api.capability.IRotorHolder; import gregtech.api.capability.impl.FluidTankList; import gregtech.api.capability.impl.MultiblockFuelRecipeLogic; -import gregtech.api.gui.GuiTextures; -import gregtech.api.gui.resources.TextureArea; import gregtech.api.metatileentity.ITieredMetaTileEntity; import gregtech.api.metatileentity.MetaTileEntity; import gregtech.api.metatileentity.interfaces.IGregTechTileEntity; import gregtech.api.metatileentity.multiblock.*; +import gregtech.api.metatileentity.multiblock.ui.MultiblockUIBuilder; +import gregtech.api.metatileentity.multiblock.ui.TemplateBarBuilder; +import gregtech.api.mui.GTGuiTextures; +import gregtech.api.mui.sync.FixedIntArraySyncValue; import gregtech.api.pattern.BlockPattern; import gregtech.api.pattern.FactoryBlockPattern; import gregtech.api.pattern.PatternMatchContext; import gregtech.api.recipes.RecipeMap; -import gregtech.api.util.TextComponentUtil; -import gregtech.api.util.TextFormattingUtil; +import gregtech.api.util.KeyUtil; import gregtech.client.renderer.ICubeRenderer; import net.minecraft.block.state.IBlockState; import net.minecraft.client.resources.I18n; import net.minecraft.item.ItemStack; import net.minecraft.util.ResourceLocation; -import net.minecraft.util.text.ITextComponent; import net.minecraft.util.text.TextFormatting; import net.minecraft.world.World; +import net.minecraftforge.fluids.Fluid; import net.minecraftforge.fluids.FluidStack; import net.minecraftforge.fluids.capability.IFluidHandler; import net.minecraftforge.fml.relauncher.Side; import net.minecraftforge.fml.relauncher.SideOnly; +import com.cleanroommc.modularui.api.drawable.IKey; +import com.cleanroommc.modularui.value.sync.IntSyncValue; +import com.cleanroommc.modularui.value.sync.PanelSyncManager; +import com.cleanroommc.modularui.value.sync.StringSyncValue; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import java.util.List; +import java.util.function.UnaryOperator; public class MetaTileEntityLargeTurbine extends FuelMultiblockController - implements ITieredMetaTileEntity, IProgressBarMultiblock { + implements ITieredMetaTileEntity, ProgressBarMultiblock { public final int tier; @@ -114,24 +120,22 @@ protected long getMaxVoltage() { } @Override - protected void addDisplayText(List textList) { + protected void configureDisplayText(MultiblockUIBuilder builder) { MultiblockFuelRecipeLogic recipeLogic = (MultiblockFuelRecipeLogic) recipeMapWorkable; - - MultiblockDisplayText.builder(textList, isStructureFormed()) - .setWorkingStatus(recipeLogic.isWorkingEnabled(), recipeLogic.isActive()) + builder.setWorkingStatus(recipeLogic.isWorkingEnabled(), recipeLogic.isActive()) .addEnergyProductionLine(getMaxVoltage(), recipeLogic.getRecipeEUt()) - .addCustom(tl -> { - if (isStructureFormed()) { - IRotorHolder rotorHolder = getRotorHolder(); - if (rotorHolder.getRotorEfficiency() > 0) { - ITextComponent efficiencyInfo = TextComponentUtil.stringWithColor( - TextFormatting.AQUA, - TextFormattingUtil.formatNumbers(rotorHolder.getTotalEfficiency()) + "%"); - tl.add(TextComponentUtil.translationWithColor( - TextFormatting.GRAY, - "gregtech.multiblock.turbine.efficiency", - efficiencyInfo)); - } + .addCustom((keyList, syncer) -> { + if (!isStructureFormed()) return; + + int rotorEfficiency = syncer.syncInt(() -> getRotorHolder().getRotorEfficiency()); + int totalEfficiency = syncer.syncInt(() -> getRotorHolder().getTotalEfficiency()); + + if (rotorEfficiency > 0) { + IKey efficiencyInfo = KeyUtil.number(TextFormatting.AQUA, + totalEfficiency, "%"); + keyList.add(KeyUtil.lang(TextFormatting.GRAY, + "gregtech.multiblock.turbine.efficiency", + efficiencyInfo)); } }) .addFuelNeededLine(recipeLogic.getRecipeFluidInputInfo(), recipeLogic.getPreviousRecipeDuration()) @@ -139,41 +143,42 @@ protected void addDisplayText(List textList) { } @Override - protected void addWarningText(List textList) { - MultiblockDisplayText.builder(textList, isStructureFormed(), false) - .addCustom(tl -> { - if (isStructureFormed()) { - IRotorHolder rotorHolder = getRotorHolder(); - if (rotorHolder.getRotorEfficiency() > 0) { - if (rotorHolder.getRotorDurabilityPercent() <= MIN_DURABILITY_TO_WARN) { - tl.add(TextComponentUtil.translationWithColor( - TextFormatting.YELLOW, - "gregtech.multiblock.turbine.rotor_durability_low")); - } - } - } - }) - .addLowDynamoTierLine(isDynamoTierTooLow()) - .addMaintenanceProblemLines(getMaintenanceProblems()); + protected void configureWarningText(MultiblockUIBuilder builder) { + builder.addCustom((keyList, syncer) -> { + if (!isStructureFormed() || syncer.syncBoolean(() -> getRotorHolder() == null)) + return; + + int rotorEfficiency = syncer.syncInt(() -> getRotorHolder().getRotorEfficiency()); + int rotorDurability = syncer.syncInt(() -> getRotorHolder().getRotorDurabilityPercent()); + + if (rotorEfficiency > 0 && rotorDurability <= MIN_DURABILITY_TO_WARN) { + keyList.add(KeyUtil.lang(TextFormatting.YELLOW, + "gregtech.multiblock.turbine.rotor_durability_low")); + } + }); + super.configureWarningText(builder); } @Override - protected void addErrorText(List textList) { - super.addErrorText(textList); - if (isStructureFormed()) { - if (!isRotorFaceFree()) { - textList.add(TextComponentUtil.translationWithColor(TextFormatting.RED, + protected void configureErrorText(MultiblockUIBuilder builder) { + super.configureErrorText(builder); + builder.addCustom((keyList, syncer) -> { + if (!isStructureFormed() || syncer.syncBoolean(() -> getRotorHolder() == null)) + return; + + if (syncer.syncBoolean(!isRotorFaceFree())) { + keyList.add(KeyUtil.lang(TextFormatting.RED, "gregtech.multiblock.turbine.obstructed")); - textList.add(TextComponentUtil.translationWithColor(TextFormatting.GRAY, + keyList.add(KeyUtil.lang(TextFormatting.GRAY, "gregtech.multiblock.turbine.obstructed.desc")); } + int rotorEfficiency = syncer.syncInt(() -> getRotorHolder().getRotorEfficiency()); - IRotorHolder rotorHolder = getRotorHolder(); - if (rotorHolder.getRotorEfficiency() <= 0) { - textList.add(TextComponentUtil.translationWithColor(TextFormatting.RED, + if (rotorEfficiency <= 0) { + keyList.add(KeyUtil.lang(TextFormatting.RED, "gregtech.multiblock.turbine.no_rotor")); } - } + }); } @Override @@ -256,114 +261,141 @@ public boolean canVoidRecipeFluidOutputs() { } @Override - protected boolean shouldShowVoidingModeButton() { + public boolean shouldShowVoidingModeButton() { return false; } @Override - public int getNumProgressBars() { + public int getProgressBarCount() { return 3; } @Override - public double getFillPercentage(int index) { - if (index == 0) { - int[] fuelAmount = new int[2]; - if (getInputFluidInventory() != null) { - MultiblockFuelRecipeLogic recipeLogic = (MultiblockFuelRecipeLogic) recipeMapWorkable; - if (recipeLogic.getInputFluidStack() != null) { - FluidStack testStack = recipeLogic.getInputFluidStack().copy(); - testStack.amount = Integer.MAX_VALUE; - fuelAmount = getTotalFluidAmount(testStack, getInputFluidInventory()); - } + public void registerBars(List> bars, PanelSyncManager syncManager) { + FixedIntArraySyncValue fuelValue = new FixedIntArraySyncValue(this::getFuelAmount, null); + StringSyncValue fuelNameValue = new StringSyncValue(() -> { + FluidStack stack = ((MultiblockFuelRecipeLogic) recipeMapWorkable).getInputFluidStack(); + if (stack == null) { + return null; } - return fuelAmount[1] != 0 ? 1.0 * fuelAmount[0] / fuelAmount[1] : 0; - } else if (index == 1) { - IRotorHolder rotorHolder = getRotorHolder(); - return rotorHolder != null ? 1.0 * rotorHolder.getRotorSpeed() / rotorHolder.getMaxRotorHolderSpeed() : 0; - } else { + Fluid fluid = stack.getFluid(); + if (fluid == null) { + return null; + } + return fluid.getName(); + }); + syncManager.syncValue("fuel_amount", fuelValue); + syncManager.syncValue("fuel_name", fuelNameValue); + + IntSyncValue rotorSpeedValue = new IntSyncValue(() -> { IRotorHolder rotorHolder = getRotorHolder(); - return rotorHolder != null ? 1.0 * rotorHolder.getRotorDurabilityPercent() / 100 : 0; - } - } + if (rotorHolder == null) { + return 0; + } + return rotorHolder.getRotorSpeed(); + }); - @Override - public TextureArea getProgressBarTexture(int index) { - if (index == 0) { - return GuiTextures.PROGRESS_BAR_LCE_FUEL; - } else if (index == 1) { - return GuiTextures.PROGRESS_BAR_TURBINE_ROTOR_SPEED; - } else { - return GuiTextures.PROGRESS_BAR_TURBINE_ROTOR_DURABILITY; - } - } + IntSyncValue rotorMaxSpeedValue = new IntSyncValue(() -> { + IRotorHolder rotorHolder = getRotorHolder(); + if (rotorHolder == null) { + return 0; + } + return rotorHolder.getMaxRotorHolderSpeed(); + }); - @Override - public void addBarHoverText(List hoverList, int index) { - if (index == 0) { - // Fuel - addFuelText(hoverList); - } else if (index == 1) { - // Rotor speed + syncManager.syncValue("rotor_speed", rotorSpeedValue); + syncManager.syncValue("rotor_max_speed", rotorMaxSpeedValue); + IntSyncValue durabilityValue = new IntSyncValue(() -> { IRotorHolder rotorHolder = getRotorHolder(); - if (rotorHolder == null || rotorHolder.getRotorEfficiency() <= 0) { - hoverList.add(TextComponentUtil.translationWithColor(TextFormatting.YELLOW, - "gregtech.multiblock.turbine.no_rotor")); - } else { - int rotorSpeed = rotorHolder.getRotorSpeed(); - int rotorMaxSpeed = rotorHolder.getMaxRotorHolderSpeed(); - ITextComponent rpmTranslated = TextComponentUtil.translationWithColor( - getRotorSpeedColor(rotorSpeed, rotorMaxSpeed), - "gregtech.multiblock.turbine.rotor_rpm_unit_name"); - ITextComponent rotorInfo = TextComponentUtil.translationWithColor( - getRotorSpeedColor(rotorSpeed, rotorMaxSpeed), - "%s / %s %s", - TextFormattingUtil.formatNumbers(rotorSpeed), - TextFormattingUtil.formatNumbers(rotorMaxSpeed), - rpmTranslated); - hoverList.add(TextComponentUtil.translationWithColor( - TextFormatting.GRAY, - "gregtech.multiblock.turbine.rotor_speed", - rotorInfo)); + if (rotorHolder == null) { + return 0; } - } else { - // Rotor durability + return rotorHolder.getRotorDurabilityPercent(); + }); + IntSyncValue efficiencyValue = new IntSyncValue(() -> { IRotorHolder rotorHolder = getRotorHolder(); - if (rotorHolder == null || rotorHolder.getRotorEfficiency() <= 0) { - // No rotor found - hoverList.add(TextComponentUtil.translationWithColor(TextFormatting.YELLOW, - "gregtech.multiblock.turbine.no_rotor")); - } else { - int rotorDurability = rotorHolder.getRotorDurabilityPercent(); - ITextComponent rotorInfo = TextComponentUtil.stringWithColor( - getRotorDurabilityColor(rotorDurability), - rotorDurability + "%"); - hoverList.add(TextComponentUtil.translationWithColor( - TextFormatting.GRAY, - "gregtech.multiblock.turbine.rotor_durability", - rotorInfo)); + if (rotorHolder == null) { + return 0; } - } - } + return rotorHolder.getRotorEfficiency(); + }); + + syncManager.syncValue("rotor_durability", durabilityValue); + syncManager.syncValue("rotor_efficiency", efficiencyValue); + + bars.add(barTest -> barTest + .progress(() -> fuelValue.getValue(1) == 0 ? 0 : + 1.0 * fuelValue.getValue(0) / fuelValue.getValue(1)) + .texture(GTGuiTextures.PROGRESS_BAR_LCE_FUEL) + .tooltipBuilder(t -> createFuelTooltip(t, fuelValue, fuelNameValue))); + + bars.add(barTest -> barTest + .progress(() -> rotorMaxSpeedValue.getIntValue() == 0 ? 0 : + 1.0 * rotorSpeedValue.getIntValue() / rotorMaxSpeedValue.getIntValue()) + .texture(GTGuiTextures.PROGRESS_BAR_TURBINE_ROTOR_SPEED) + .tooltipBuilder(t -> { + if (isStructureFormed()) { + int speed = rotorSpeedValue.getIntValue(); + int maxSpeed = rotorMaxSpeedValue.getIntValue(); - private TextFormatting getRotorDurabilityColor(int durability) { - if (durability > 40) { - return TextFormatting.GREEN; - } else if (durability > MIN_DURABILITY_TO_WARN) { - return TextFormatting.YELLOW; - } else { - return TextFormatting.RED; - } + t.addLine(KeyUtil.lang("gregtech.multiblock.turbine.rotor_speed", + getSpeedFormat(maxSpeed, speed), speed, maxSpeed)); + } else { + t.addLine(IKey.lang("gregtech.multiblock.invalid_structure")); + } + })); + + bars.add(barTest -> barTest + .progress(() -> durabilityValue.getIntValue() / 100.0) + .texture(GTGuiTextures.PROGRESS_BAR_TURBINE_ROTOR_DURABILITY) + .tooltipBuilder(t -> { + if (isStructureFormed()) { + if (efficiencyValue.getIntValue() <= 0) { + t.addLine(IKey.lang("gregtech.multiblock.turbine.no_rotor")); + } else { + int durability = durabilityValue.getIntValue(); + // TODO working dynamic color substitutions into IKey.lang + if (durability > 40) { + t.addLine(IKey.lang("gregtech.multiblock.turbine.rotor_durability.high", + durability)); + } else if (durability > MIN_DURABILITY_TO_WARN) { + t.addLine(IKey.lang("gregtech.multiblock.turbine.rotor_durability.medium", + durability)); + } else { + t.addLine(IKey.lang("gregtech.multiblock.turbine.rotor_durability.low", + durability)); + } + } + } else { + t.addLine(IKey.lang("gregtech.multiblock.invalid_structure")); + } + })); } - private TextFormatting getRotorSpeedColor(int rotorSpeed, int maxRotorSpeed) { - double speedRatio = 1.0 * rotorSpeed / maxRotorSpeed; - if (speedRatio < 0.4) { + private @NotNull TextFormatting getSpeedFormat(int maxSpeed, int speed) { + float percent = maxSpeed == 0 ? 0 : 1.0f * speed / maxSpeed; + + if (percent < 0.4) { return TextFormatting.RED; - } else if (speedRatio < 0.8) { + } else if (percent < 0.8) { return TextFormatting.YELLOW; } else { return TextFormatting.GREEN; } } + + /** + * @return an array of [fuel stored, fuel capacity] + */ + private int[] getFuelAmount() { + if (getInputFluidInventory() != null) { + MultiblockFuelRecipeLogic recipeLogic = (MultiblockFuelRecipeLogic) recipeMapWorkable; + if (recipeLogic.getInputFluidStack() != null) { + FluidStack testStack = recipeLogic.getInputFluidStack().copy(); + testStack.amount = Integer.MAX_VALUE; + return getTotalFluidAmount(testStack, getInputFluidInventory()); + } + } + return new int[2]; + } } diff --git a/src/main/java/gregtech/common/metatileentities/multi/multiblockpart/MetaTileEntityFluidHatch.java b/src/main/java/gregtech/common/metatileentities/multi/multiblockpart/MetaTileEntityFluidHatch.java index 5537385473a..2c11138db0c 100644 --- a/src/main/java/gregtech/common/metatileentities/multi/multiblockpart/MetaTileEntityFluidHatch.java +++ b/src/main/java/gregtech/common/metatileentities/multi/multiblockpart/MetaTileEntityFluidHatch.java @@ -31,6 +31,7 @@ import net.minecraft.network.PacketBuffer; import net.minecraft.util.EnumFacing; import net.minecraft.util.ResourceLocation; +import net.minecraft.util.text.TextFormatting; import net.minecraft.world.World; import net.minecraftforge.common.capabilities.Capability; import net.minecraftforge.fluids.FluidStack; @@ -192,6 +193,7 @@ public void writeInitialSyncData(PacketBuffer buf) { buf.writeBoolean(workingEnabled); if (isExportHatch) { buf.writeBoolean(locked); + NetworkUtils.writeFluidStack(buf, lockedFluid); } else { buf.writeVarInt(this.circuitInventory.getCircuitValue()); } @@ -203,6 +205,7 @@ public void receiveInitialSyncData(PacketBuffer buf) { this.workingEnabled = buf.readBoolean(); if (isExportHatch) { this.locked = buf.readBoolean(); + this.lockedFluid = NetworkUtils.readFluidStack(buf); } else { setGhostCircuitConfig(buf.readVarInt()); } @@ -280,13 +283,16 @@ public boolean usesMui2() { @Override public ModularPanel buildUI(PosGuiData guiData, PanelSyncManager guiSyncManager) { var fluidSyncHandler = GTFluidSlot.sync(fluidTank) - .showAmount(false) - .accessibility(true, !isExportHatch) - .handleLocking(() -> this.lockedFluid, fluidStack -> { - setLocked(fluidStack != null); - this.lockedFluid = fluidStack; - this.fluidTank.onContentsChanged(); - }, this::setLocked); + .showAmountOnSlot(false) + .accessibility(true, !isExportHatch); + + if (isExportHatch) { + fluidSyncHandler.handleLocking(() -> this.lockedFluid, fluidStack -> { + setLocked(fluidStack != null); + this.lockedFluid = fluidStack; + this.fluidTank.onContentsChanged(); + }, this::setLocked); + } return GTGuis.createPanel(this, 176, 166) .child(IKey.lang(getMetaFullName()).asWidget().pos(6, 6)) @@ -329,11 +335,16 @@ public ModularPanel buildUI(PosGuiData guiData, PanelSyncManager guiSyncManager) .autoUpdate(true) .textBuilder(richText -> { richText.addLine(IKey.lang("gregtech.gui.fluid_amount")); - String name = fluidSyncHandler.getFluidLocalizedName(); - if (name == null) return; - if (name.length() > 25) name = name.substring(0, 25) + "..."; - richText.addLine(IKey.str(name)); + IKey nameKey = fluidSyncHandler.getFluidNameKey(); + if (nameKey == IKey.EMPTY) return; + + String formatted = nameKey.getFormatted(); + if (formatted.length() > 25) { + nameKey = IKey.str(formatted.substring(0, 25) + TextFormatting.WHITE + "..."); + } + + richText.addLine(nameKey); richText.addLine(IKey.str(fluidSyncHandler.getFormattedFluidAmount())); })) .child(new GTFluidSlot() diff --git a/src/main/java/gregtech/common/metatileentities/multi/multiblockpart/MetaTileEntityItemBus.java b/src/main/java/gregtech/common/metatileentities/multi/multiblockpart/MetaTileEntityItemBus.java index ce931470e6a..c3dae2f0e06 100644 --- a/src/main/java/gregtech/common/metatileentities/multi/multiblockpart/MetaTileEntityItemBus.java +++ b/src/main/java/gregtech/common/metatileentities/multi/multiblockpart/MetaTileEntityItemBus.java @@ -38,10 +38,8 @@ import codechicken.lib.render.pipeline.IVertexOperation; import codechicken.lib.vec.Matrix4; import com.cleanroommc.modularui.api.drawable.IKey; -import com.cleanroommc.modularui.api.widget.IWidget; import com.cleanroommc.modularui.factory.PosGuiData; import com.cleanroommc.modularui.screen.ModularPanel; -import com.cleanroommc.modularui.value.BoolValue; import com.cleanroommc.modularui.value.sync.BooleanSyncValue; import com.cleanroommc.modularui.value.sync.PanelSyncManager; import com.cleanroommc.modularui.value.sync.SyncHandlers; @@ -267,40 +265,19 @@ public boolean usesMui2() { } @Override - public ModularPanel buildUI(PosGuiData guiData, PanelSyncManager guiSyncManager) { + public ModularPanel buildUI(PosGuiData guiData, PanelSyncManager panelSyncManager) { int rowSize = (int) Math.sqrt(getInventorySize()); - guiSyncManager.registerSlotGroup("item_inv", rowSize); + panelSyncManager.registerSlotGroup("item_inv", rowSize); int backgroundWidth = Math.max( 9 * 18 + 18 + 14 + 5, // Player Inv width rowSize * 18 + 14); // Bus Inv width int backgroundHeight = 18 + 18 * rowSize + 94; - List> widgets = new ArrayList<>(); - for (int i = 0; i < rowSize; i++) { - widgets.add(new ArrayList<>()); - for (int j = 0; j < rowSize; j++) { - int index = i * rowSize + j; - IItemHandlerModifiable handler = isExportHatch ? exportItems : importItems; - widgets.get(i) - .add(new ItemSlot() - .slot(SyncHandlers.itemSlot(handler, index) - .slotGroup("item_inv") - .changeListener((newItem, onlyAmountChanged, client, init) -> { - if (onlyAmountChanged && - handler instanceof GTItemStackHandler gtHandler) { - gtHandler.onContentsChanged(index); - } - }) - .accessibility(!isExportHatch, true))); - } - } - BooleanSyncValue workingStateValue = new BooleanSyncValue(() -> workingEnabled, val -> workingEnabled = val); - guiSyncManager.syncValue("working_state", workingStateValue); BooleanSyncValue collapseStateValue = new BooleanSyncValue(() -> autoCollapse, val -> autoCollapse = val); - guiSyncManager.syncValue("collapse_state", collapseStateValue); + IItemHandlerModifiable handler = isExportHatch ? exportItems : importItems; boolean hasGhostCircuit = hasGhostCircuitInventory() && this.circuitInventory != null; return GTGuis.createPanel(this, backgroundWidth, backgroundHeight) @@ -311,33 +288,40 @@ public ModularPanel buildUI(PosGuiData guiData, PanelSyncManager guiSyncManager) .minElementMargin(0, 0) .minColWidth(18).minRowHeight(18) .alignX(0.5f) - .matrix(widgets)) + .mapTo(rowSize, rowSize * rowSize, index -> new ItemSlot() + .slot(SyncHandlers.itemSlot(handler, index) + .slotGroup("item_inv") + .changeListener((newItem, onlyAmountChanged, client, init) -> { + if (onlyAmountChanged && + handler instanceof GTItemStackHandler gtHandler) { + gtHandler.onContentsChanged(index); + } + }) + .accessibility(!isExportHatch, true)))) .child(Flow.column() .pos(backgroundWidth - 7 - 18, backgroundHeight - 18 * 4 - 7 - 5) .width(18).height(18 * 4 + 5) .child(GTGuiTextures.getLogo(getUITheme()).asWidget().size(17).top(18 * 3 + 5)) .child(new ToggleButton() .top(18 * 2) - .value(new BoolValue.Dynamic(workingStateValue::getBoolValue, - workingStateValue::setBoolValue)) + .value(workingStateValue) .overlay(GTGuiTextures.BUTTON_ITEM_OUTPUT) - .tooltipBuilder(t -> t.setAutoUpdate(true) - .addLine(isExportHatch ? - (workingStateValue.getBoolValue() ? - IKey.lang("gregtech.gui.item_auto_output.tooltip.enabled") : - IKey.lang("gregtech.gui.item_auto_output.tooltip.disabled")) : - (workingStateValue.getBoolValue() ? - IKey.lang("gregtech.gui.item_auto_input.tooltip.enabled") : - IKey.lang("gregtech.gui.item_auto_input.tooltip.disabled"))))) + .tooltipAutoUpdate(true) + .tooltipBuilder(t -> t.addLine(isExportHatch ? + (workingStateValue.getBoolValue() ? + IKey.lang("gregtech.gui.item_auto_output.tooltip.enabled") : + IKey.lang("gregtech.gui.item_auto_output.tooltip.disabled")) : + (workingStateValue.getBoolValue() ? + IKey.lang("gregtech.gui.item_auto_input.tooltip.enabled") : + IKey.lang("gregtech.gui.item_auto_input.tooltip.disabled"))))) .child(new ToggleButton() .top(18) - .value(new BoolValue.Dynamic(collapseStateValue::getBoolValue, - collapseStateValue::setBoolValue)) + .value(collapseStateValue) .overlay(GTGuiTextures.BUTTON_AUTO_COLLAPSE) - .tooltipBuilder(t -> t.setAutoUpdate(true) - .addLine(collapseStateValue.getBoolValue() ? - IKey.lang("gregtech.gui.item_auto_collapse.tooltip.enabled") : - IKey.lang("gregtech.gui.item_auto_collapse.tooltip.disabled")))) + .tooltipAutoUpdate(true) + .tooltipBuilder(t -> t.addLine(collapseStateValue.getBoolValue() ? + IKey.lang("gregtech.gui.item_auto_collapse.tooltip.enabled") : + IKey.lang("gregtech.gui.item_auto_collapse.tooltip.disabled")))) .childIf(hasGhostCircuit, new GhostCircuitSlotWidget() .slot(SyncHandlers.itemSlot(circuitInventory, 0)) .background(GTGuiTextures.SLOT, GTGuiTextures.INT_CIRCUIT_OVERLAY)) diff --git a/src/main/java/gregtech/common/metatileentities/multi/multiblockpart/MetaTileEntityMufflerHatch.java b/src/main/java/gregtech/common/metatileentities/multi/multiblockpart/MetaTileEntityMufflerHatch.java index c9342d0c3a0..d6337c70ca3 100644 --- a/src/main/java/gregtech/common/metatileentities/multi/multiblockpart/MetaTileEntityMufflerHatch.java +++ b/src/main/java/gregtech/common/metatileentities/multi/multiblockpart/MetaTileEntityMufflerHatch.java @@ -1,5 +1,6 @@ package gregtech.common.metatileentities.multi.multiblockpart; +import gregtech.api.capability.GregtechDataCodes; import gregtech.api.capability.IMufflerHatch; import gregtech.api.metatileentity.ITieredMetaTileEntity; import gregtech.api.metatileentity.MetaTileEntity; @@ -13,6 +14,7 @@ import net.minecraft.block.state.IBlockState; import net.minecraft.client.resources.I18n; import net.minecraft.item.ItemStack; +import net.minecraft.network.PacketBuffer; import net.minecraft.util.ResourceLocation; import net.minecraft.util.math.BlockPos; import net.minecraft.world.World; @@ -47,7 +49,12 @@ public void update() { VanillaParticleEffects.mufflerEffect(this, controller.getMufflerParticle()); } } else if (getOffsetTimer() % 10 == 0) { - this.frontFaceFree = checkFrontFaceFree(); + boolean frontFaceFree = checkFrontFaceFree(); + if (frontFaceFree != this.frontFaceFree) { + this.frontFaceFree = frontFaceFree; + writeCustomData(GregtechDataCodes.MUFFLER_OBSTRUCTED, + buffer -> buffer.writeBoolean(this.frontFaceFree)); + } } } @@ -73,6 +80,26 @@ private boolean checkFrontFaceFree() { return blockState.getBlock().isAir(blockState, getWorld(), frontPos) || GTUtility.isBlockSnow(blockState); } + @Override + public void writeInitialSyncData(PacketBuffer buf) { + super.writeInitialSyncData(buf); + buf.writeBoolean(this.frontFaceFree); + } + + @Override + public void receiveInitialSyncData(PacketBuffer buf) { + super.receiveInitialSyncData(buf); + this.frontFaceFree = buf.readBoolean(); + } + + @Override + public void receiveCustomData(int dataId, PacketBuffer buf) { + super.receiveCustomData(dataId, buf); + if (dataId == GregtechDataCodes.MUFFLER_OBSTRUCTED) { + this.frontFaceFree = buf.readBoolean(); + } + } + @Override public void renderMetaTileEntity(CCRenderState renderState, Matrix4 translation, IVertexOperation[] pipeline) { super.renderMetaTileEntity(renderState, translation, pipeline); diff --git a/src/main/java/gregtech/common/metatileentities/multi/multiblockpart/MetaTileEntityReservoirHatch.java b/src/main/java/gregtech/common/metatileentities/multi/multiblockpart/MetaTileEntityReservoirHatch.java index 83488327c26..9f8a133d401 100644 --- a/src/main/java/gregtech/common/metatileentities/multi/multiblockpart/MetaTileEntityReservoirHatch.java +++ b/src/main/java/gregtech/common/metatileentities/multi/multiblockpart/MetaTileEntityReservoirHatch.java @@ -153,7 +153,7 @@ public ModularPanel buildUI(PosGuiData guiData, PanelSyncManager guiSyncManager) guiSyncManager.registerSlotGroup("item_inv", 2); GTFluidSyncHandler tankSyncHandler = GTFluidSlot.sync(this.fluidTank) - .showAmount(false) + .showAmountOnSlot(false) .accessibility(true, false); // TODO: Change the position of the name when it's standardized. diff --git a/src/main/java/gregtech/common/metatileentities/multi/multiblockpart/hpca/MetaTileEntityHPCABridge.java b/src/main/java/gregtech/common/metatileentities/multi/multiblockpart/hpca/MetaTileEntityHPCABridge.java index 9af74c4227b..7bc26b5edc9 100644 --- a/src/main/java/gregtech/common/metatileentities/multi/multiblockpart/hpca/MetaTileEntityHPCABridge.java +++ b/src/main/java/gregtech/common/metatileentities/multi/multiblockpart/hpca/MetaTileEntityHPCABridge.java @@ -1,15 +1,16 @@ package gregtech.common.metatileentities.multi.multiblockpart.hpca; import gregtech.api.GTValues; -import gregtech.api.gui.GuiTextures; -import gregtech.api.gui.resources.TextureArea; import gregtech.api.metatileentity.MetaTileEntity; import gregtech.api.metatileentity.interfaces.IGregTechTileEntity; +import gregtech.api.mui.GTGuiTextures; import gregtech.client.renderer.texture.Textures; import gregtech.client.renderer.texture.cube.SimpleOverlayRenderer; import net.minecraft.util.ResourceLocation; +import com.cleanroommc.modularui.drawable.UITexture; + public class MetaTileEntityHPCABridge extends MetaTileEntityHPCAComponent { public MetaTileEntityHPCABridge(ResourceLocation metaTileEntityId) { @@ -37,8 +38,8 @@ public SimpleOverlayRenderer getFrontOverlay() { } @Override - public TextureArea getComponentIcon() { - return GuiTextures.HPCA_ICON_BRIDGE_COMPONENT; + public UITexture getComponentIcon() { + return GTGuiTextures.HPCA_ICON_BRIDGE_COMPONENT; } @Override diff --git a/src/main/java/gregtech/common/metatileentities/multi/multiblockpart/hpca/MetaTileEntityHPCAComputation.java b/src/main/java/gregtech/common/metatileentities/multi/multiblockpart/hpca/MetaTileEntityHPCAComputation.java index a27376901c9..9d8ebade08e 100644 --- a/src/main/java/gregtech/common/metatileentities/multi/multiblockpart/hpca/MetaTileEntityHPCAComputation.java +++ b/src/main/java/gregtech/common/metatileentities/multi/multiblockpart/hpca/MetaTileEntityHPCAComputation.java @@ -2,15 +2,16 @@ import gregtech.api.GTValues; import gregtech.api.capability.IHPCAComputationProvider; -import gregtech.api.gui.GuiTextures; -import gregtech.api.gui.resources.TextureArea; import gregtech.api.metatileentity.MetaTileEntity; import gregtech.api.metatileentity.interfaces.IGregTechTileEntity; +import gregtech.api.mui.GTGuiTextures; import gregtech.client.renderer.texture.Textures; import gregtech.client.renderer.texture.cube.SimpleOverlayRenderer; import net.minecraft.util.ResourceLocation; +import com.cleanroommc.modularui.drawable.UITexture; + public class MetaTileEntityHPCAComputation extends MetaTileEntityHPCAComponent implements IHPCAComputationProvider { private final boolean advanced; @@ -37,13 +38,13 @@ public SimpleOverlayRenderer getFrontOverlay() { } @Override - public TextureArea getComponentIcon() { + public UITexture getComponentIcon() { if (isDamaged()) { - return advanced ? GuiTextures.HPCA_ICON_DAMAGED_ADVANCED_COMPUTATION_COMPONENT : - GuiTextures.HPCA_ICON_DAMAGED_COMPUTATION_COMPONENT; + return advanced ? GTGuiTextures.HPCA_ICON_DAMAGED_ADVANCED_COMPUTATION_COMPONENT : + GTGuiTextures.HPCA_ICON_DAMAGED_COMPUTATION_COMPONENT; } - return advanced ? GuiTextures.HPCA_ICON_ADVANCED_COMPUTATION_COMPONENT : - GuiTextures.HPCA_ICON_COMPUTATION_COMPONENT; + return advanced ? GTGuiTextures.HPCA_ICON_ADVANCED_COMPUTATION_COMPONENT : + GTGuiTextures.HPCA_ICON_COMPUTATION_COMPONENT; } @Override diff --git a/src/main/java/gregtech/common/metatileentities/multi/multiblockpart/hpca/MetaTileEntityHPCACooler.java b/src/main/java/gregtech/common/metatileentities/multi/multiblockpart/hpca/MetaTileEntityHPCACooler.java index 90d00239f88..100960eab92 100644 --- a/src/main/java/gregtech/common/metatileentities/multi/multiblockpart/hpca/MetaTileEntityHPCACooler.java +++ b/src/main/java/gregtech/common/metatileentities/multi/multiblockpart/hpca/MetaTileEntityHPCACooler.java @@ -2,15 +2,16 @@ import gregtech.api.GTValues; import gregtech.api.capability.IHPCACoolantProvider; -import gregtech.api.gui.GuiTextures; -import gregtech.api.gui.resources.TextureArea; import gregtech.api.metatileentity.MetaTileEntity; import gregtech.api.metatileentity.interfaces.IGregTechTileEntity; +import gregtech.api.mui.GTGuiTextures; import gregtech.client.renderer.texture.Textures; import gregtech.client.renderer.texture.cube.SimpleOverlayRenderer; import net.minecraft.util.ResourceLocation; +import com.cleanroommc.modularui.drawable.UITexture; + public class MetaTileEntityHPCACooler extends MetaTileEntityHPCAComponent implements IHPCACoolantProvider { private final boolean advanced; @@ -36,8 +37,8 @@ public SimpleOverlayRenderer getFrontOverlay() { } @Override - public TextureArea getComponentIcon() { - return advanced ? GuiTextures.HPCA_ICON_ACTIVE_COOLER_COMPONENT : GuiTextures.HPCA_ICON_HEAT_SINK_COMPONENT; + public UITexture getComponentIcon() { + return advanced ? GTGuiTextures.HPCA_ICON_ACTIVE_COOLER_COMPONENT : GTGuiTextures.HPCA_ICON_HEAT_SINK_COMPONENT; } @Override diff --git a/src/main/java/gregtech/common/metatileentities/multi/multiblockpart/hpca/MetaTileEntityHPCAEmpty.java b/src/main/java/gregtech/common/metatileentities/multi/multiblockpart/hpca/MetaTileEntityHPCAEmpty.java index 9c25261401e..fdd8cb62050 100644 --- a/src/main/java/gregtech/common/metatileentities/multi/multiblockpart/hpca/MetaTileEntityHPCAEmpty.java +++ b/src/main/java/gregtech/common/metatileentities/multi/multiblockpart/hpca/MetaTileEntityHPCAEmpty.java @@ -1,7 +1,5 @@ package gregtech.common.metatileentities.multi.multiblockpart.hpca; -import gregtech.api.gui.GuiTextures; -import gregtech.api.gui.resources.TextureArea; import gregtech.api.metatileentity.MetaTileEntity; import gregtech.api.metatileentity.interfaces.IGregTechTileEntity; import gregtech.client.renderer.texture.Textures; @@ -30,11 +28,6 @@ public SimpleOverlayRenderer getFrontOverlay() { return Textures.HPCA_EMPTY_OVERLAY; } - @Override - public TextureArea getComponentIcon() { - return GuiTextures.HPCA_ICON_EMPTY_COMPONENT; - } - @Override public int getUpkeepEUt() { return 0; diff --git a/src/main/java/gregtech/common/metatileentities/steam/multiblockpart/MetaTileEntitySteamHatch.java b/src/main/java/gregtech/common/metatileentities/steam/multiblockpart/MetaTileEntitySteamHatch.java index bacb525a3aa..78bc61b5a2d 100644 --- a/src/main/java/gregtech/common/metatileentities/steam/multiblockpart/MetaTileEntitySteamHatch.java +++ b/src/main/java/gregtech/common/metatileentities/steam/multiblockpart/MetaTileEntitySteamHatch.java @@ -144,7 +144,7 @@ public ModularPanel buildUI(PosGuiData guiData, PanelSyncManager guiSyncManager) guiSyncManager.registerSlotGroup("item_inv", 2); GTFluidSyncHandler tankSyncHandler = GTFluidSlot.sync(this.importFluids.getTankAt(0)) - .showAmount(false); + .showAmountOnSlot(false); return GTGuis.createPanel(this, 176, 166) .child(IKey.lang(getMetaFullName()).asWidget().pos(5, 5)) diff --git a/src/main/java/gregtech/common/metatileentities/steam/multiblockpart/MetaTileEntitySteamItemBus.java b/src/main/java/gregtech/common/metatileentities/steam/multiblockpart/MetaTileEntitySteamItemBus.java index 633ca974bac..c73bcb442a9 100644 --- a/src/main/java/gregtech/common/metatileentities/steam/multiblockpart/MetaTileEntitySteamItemBus.java +++ b/src/main/java/gregtech/common/metatileentities/steam/multiblockpart/MetaTileEntitySteamItemBus.java @@ -87,8 +87,8 @@ public void renderMetaTileEntity(CCRenderState renderState, Matrix4 translation, } @Override - public ModularPanel buildUI(PosGuiData guiData, PanelSyncManager guiSyncManager) { - guiSyncManager.registerSlotGroup("item_inv", 2); + public ModularPanel buildUI(PosGuiData guiData, PanelSyncManager panelSyncManager) { + panelSyncManager.registerSlotGroup("item_inv", 2); List> widgets = new ArrayList<>(); for (int i = 0; i < 2; i++) { diff --git a/src/main/java/gregtech/common/metatileentities/storage/MetaTileEntityCrate.java b/src/main/java/gregtech/common/metatileentities/storage/MetaTileEntityCrate.java index d837e3bcd80..e4a8392b074 100644 --- a/src/main/java/gregtech/common/metatileentities/storage/MetaTileEntityCrate.java +++ b/src/main/java/gregtech/common/metatileentities/storage/MetaTileEntityCrate.java @@ -140,8 +140,8 @@ public boolean usesMui2() { } @Override - public ModularPanel buildUI(PosGuiData guiData, PanelSyncManager guiSyncManager) { - guiSyncManager.registerSlotGroup("item_inv", rowSize); + public ModularPanel buildUI(PosGuiData guiData, PanelSyncManager panelSyncManager) { + panelSyncManager.registerSlotGroup("item_inv", rowSize); int rows = inventorySize / rowSize; List> widgets = new ArrayList<>(); diff --git a/src/main/java/gregtech/common/mui/widget/GTFluidSlot.java b/src/main/java/gregtech/common/mui/widget/GTFluidSlot.java index bc9ba458098..79c31bd31cd 100644 --- a/src/main/java/gregtech/common/mui/widget/GTFluidSlot.java +++ b/src/main/java/gregtech/common/mui/widget/GTFluidSlot.java @@ -1,22 +1,16 @@ package gregtech.common.mui.widget; -import gregtech.api.GTValues; import gregtech.api.mui.sync.GTFluidSyncHandler; -import gregtech.api.util.FluidTooltipUtil; import gregtech.api.util.GTUtility; -import gregtech.api.util.LocalizationUtils; import gregtech.client.utils.RenderUtil; -import gregtech.client.utils.TooltipHelper; import net.minecraft.client.renderer.GlStateManager; import net.minecraft.item.ItemStack; -import net.minecraft.util.text.TextFormatting; import net.minecraftforge.fluids.FluidStack; import net.minecraftforge.fluids.IFluidTank; import net.minecraftforge.fluids.capability.CapabilityFluidHandler; import com.cleanroommc.modularui.api.ITheme; -import com.cleanroommc.modularui.api.drawable.IKey; import com.cleanroommc.modularui.api.widget.Interactable; import com.cleanroommc.modularui.drawable.GuiDraw; import com.cleanroommc.modularui.drawable.text.TextRenderer; @@ -24,7 +18,6 @@ import com.cleanroommc.modularui.integration.jei.JeiIngredientProvider; import com.cleanroommc.modularui.network.NetworkUtils; import com.cleanroommc.modularui.screen.ModularScreen; -import com.cleanroommc.modularui.screen.RichTooltip; import com.cleanroommc.modularui.screen.viewport.ModularGuiContext; import com.cleanroommc.modularui.theme.WidgetSlotTheme; import com.cleanroommc.modularui.theme.WidgetTheme; @@ -45,32 +38,10 @@ public final class GTFluidSlot extends Widget implements Interactab private boolean disableBackground = false; public GTFluidSlot() { - tooltip().setAutoUpdate(true); - // .setHasTitleMargin(true); + tooltip().setAutoUpdate(true).titleMargin(); tooltipBuilder(tooltip -> { if (!isSynced()) return; - var fluid = this.syncHandler.getFluid(); - - if (fluid == null) - fluid = this.syncHandler.getLockedFluid(); - - if (fluid == null) return; - - tooltip.addLine(IKey.str(fluid.getLocalizedName())); - if (this.syncHandler.showAmount()) - tooltip.addLine(IKey.lang("gregtech.fluid.amount", fluid.amount, this.syncHandler.getCapacity())); - - if (this.syncHandler.isPhantom() && this.syncHandler.showAmount()) - tooltip.addLine(IKey.lang("modularui.fluid.phantom.control")); - - // Add various tooltips from the material - for (String s : FluidTooltipUtil.getFluidTooltip(fluid)) { - if (s.isEmpty()) continue; - tooltip.addLine(IKey.str(s)); - } - - if (this.syncHandler.showAmount()) - addIngotMolFluidTooltip(fluid, tooltip); + syncHandler.handleTooltip(tooltip); }); } @@ -83,7 +54,9 @@ public void onInit() { this.textRenderer.setShadow(true); this.textRenderer.setScale(0.5f); this.textRenderer.setColor(Color.WHITE.main); - getContext().getJeiSettings().addJeiGhostIngredientSlot(this); + if (syncHandler.canLockFluid() || syncHandler.isPhantom()) { + getContext().getJeiSettings().addJeiGhostIngredientSlot(this); + } } public GTFluidSlot syncHandler(IFluidTank fluidTank) { @@ -124,9 +97,19 @@ public void draw(ModularGuiContext context, WidgetSlotTheme widgetTheme) { if (content == null) content = this.syncHandler.getLockedFluid(); - GuiDraw.drawFluidTexture(content, 1, 1, getArea().w() - 2, getArea().h() - 2, 0); + float height = getArea().h() - 2; + int y = 1; - if (content != null && this.syncHandler.showAmount()) { + if (!this.syncHandler.drawAlwaysFull()) { + float amt = content == null ? 0f : content.amount; + float newHeight = height * (amt / this.syncHandler.getCapacity()); + y += (int) (height - newHeight); + height = newHeight; + } + + GuiDraw.drawFluidTexture(content, 1, y, getArea().w() - 2, height, 0); + + if (content != null && this.syncHandler.showAmountOnSlot()) { String amount = NumberFormat.formatWithMaxDigits(content.amount, 3) + "L"; this.textRenderer.setAlignment(Alignment.CenterRight, getArea().width - 1f); this.textRenderer.setPos(0, 12); @@ -173,19 +156,6 @@ protected WidgetTheme getWidgetThemeInternal(ITheme theme) { return theme.getFluidSlotTheme(); } - public static void addIngotMolFluidTooltip(FluidStack fluidStack, RichTooltip tooltip) { - // Add tooltip showing how many "ingot moles" (increments of 144) this fluid is if shift is held - if (TooltipHelper.isShiftDown() && fluidStack.amount > GTValues.L) { - int numIngots = fluidStack.amount / GTValues.L; - int extra = fluidStack.amount % GTValues.L; - String fluidAmount = String.format(" %,d L = %,d * %d L", fluidStack.amount, numIngots, GTValues.L); - if (extra != 0) { - fluidAmount += String.format(" + %d L", extra); - } - tooltip.add(TextFormatting.GRAY + LocalizationUtils.format("gregtech.gui.amount_raw") + fluidAmount); - } - } - @Override public void setGhostIngredient(@NotNull FluidStack ingredient) { if (this.syncHandler.isPhantom()) { @@ -199,6 +169,8 @@ public void setGhostIngredient(@NotNull FluidStack ingredient) { @Override public @Nullable FluidStack castGhostIngredientIfValid(@NotNull Object ingredient) { + if (!(syncHandler.canLockFluid() || syncHandler.isPhantom())) return null; + if (ingredient instanceof FluidStack stack) { return stack; } else if (ingredient instanceof ItemStack stack && diff --git a/src/main/java/gregtech/common/mui/widget/ScrollableTextWidget.java b/src/main/java/gregtech/common/mui/widget/ScrollableTextWidget.java new file mode 100644 index 00000000000..a20bfc92a41 --- /dev/null +++ b/src/main/java/gregtech/common/mui/widget/ScrollableTextWidget.java @@ -0,0 +1,231 @@ +package gregtech.common.mui.widget; + +import gregtech.api.mui.IconAcessor; + +import net.minecraft.client.gui.FontRenderer; + +import com.cleanroommc.modularui.api.GuiAxis; +import com.cleanroommc.modularui.api.drawable.IHoverable; +import com.cleanroommc.modularui.api.drawable.IRichTextBuilder; +import com.cleanroommc.modularui.api.layout.IViewport; +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.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; +import com.cleanroommc.modularui.utils.HoveredWidgetList; +import com.cleanroommc.modularui.widget.Widget; +import com.cleanroommc.modularui.widget.scroll.ScrollArea; +import com.cleanroommc.modularui.widget.scroll.ScrollData; +import com.cleanroommc.modularui.widget.sizer.Area; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import java.util.function.Consumer; + +public class ScrollableTextWidget extends Widget + 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(); + + public ScrollableTextWidget() { + listenGuiAction((IGuiAction.MouseReleased) mouseButton -> { + this.scroll.mouseReleased(getContext()); + return false; + }); + } + + @Override + public void onInit() { + this.scroll.setScrollData(ScrollData.of(GuiAxis.Y)); + } + + @Override + public void onUpdate() { + super.onUpdate(); + this.scroll.drag(getContext().getAbsMouseX(), getContext().getAbsMouseY()); + } + + public void markDirty() { + this.dirty = true; + } + + @Override + public void drawForeground(ModularGuiContext context) { + super.drawForeground(context); + if (getHoveredElement() instanceof IHoverable hoverable) { + hoverable.onHover(); + RichTooltip tooltip = hoverable.getTooltip(); + if (tooltip != null) { + tooltip.draw(context); + } + } + if (getHoveredElement() instanceof IconAcessor accessor && + accessor.gregTech$getDrawable() instanceof JeiIngredientProvider provider) { + lastIngredient = provider.getIngredient(); + } else { + lastIngredient = null; + } + } + + @Override + public @NotNull Result onMousePressed(int mouseButton) { + ModularGuiContext context = getContext(); + if (this.scroll.mouseClicked(context)) { + return Result.STOP; + } + if (getHoveredElement() instanceof Interactable interactable) { + return interactable.onMousePressed(mouseButton); + } + return Result.ACCEPT; + } + + @Override + public boolean onMouseScroll(ModularScreen.UpOrDown scrollDirection, int amount) { + if (this.scroll.mouseScroll(getContext())) { + return true; + } + if (getHoveredElement() instanceof Interactable interactable) { + return interactable.onMouseScroll(scrollDirection, amount); + } + return false; + } + + @Override + public boolean onMouseRelease(int mouseButton) { + this.scroll.mouseReleased(getContext()); + if (getHoveredElement() instanceof Interactable interactable) { + return interactable.onMouseRelease(mouseButton); + } + return false; + } + + @Nullable + public Object getHoveredElement() { + if (!isHovering()) return null; + FontRenderer fr = getContext().getFontRenderer(); + int x = getContext().getMouseX(), y = getContext().getMouseY(); + return this.text.getHoveringElement(fr, x, y + getScrollY()); // undo scrolling + } + + @Override + public Area getArea() { + return getScrollArea(); + } + + public ScrollArea getScrollArea() { + return this.scroll; + } + + @Override + public void transformChildren(IViewportStack stack) { + stack.translate(0, -getScrollY()); + } + + @Override + public void getSelfAt(IViewportStack stack, HoveredWidgetList widgets, int x, int y) { + if (isInside(stack, x, y)) { + widgets.add(this, stack.peek()); + } + } + + @Override + public void getWidgetsAt(IViewportStack stack, HoveredWidgetList widgets, int x, int y) {} + + @Override + public void onResized() { + this.scroll.getScrollY().clamp(this.scroll); + } + + @Override + public boolean canHover() { + return super.canHover() || + this.scroll.isInsideScrollbarArea(getContext().getMouseX(), getContext().getMouseY()); + } + + @Override + public void preDraw(ModularGuiContext context, boolean transformed) { + if (!transformed) { + Stencil.applyAtZero(this.scroll, context); + } else { + drawText(context); + } + } + + private void drawText(ModularGuiContext context) { + if (this.autoUpdate || this.dirty) { + if (this.builder != null) { + this.text.clearText(); + this.builder.accept(this.text); + } + this.dirty = false; + } + this.text.setupRenderer(this.renderer, getArea().getPadding().left, getArea().getPadding().top - getScrollY(), + getArea().paddedWidth(), getArea().paddedHeight(), + getWidgetTheme(context.getTheme()).getTextColor(), + getWidgetTheme(context.getTheme()).getTextShadow()); + this.text.compileAndDraw(this.renderer, context, false); + // this isn't perfect, but i hope it's good enough + int diff = (int) Math.ceil((this.renderer.getLastHeight() - getArea().h()) / 2); + this.scroll.getScrollY().setScrollSize(getArea().h() + Math.max(0, diff)); + } + + @Override + public void postDraw(ModularGuiContext context, boolean transformed) { + if (!transformed) { + Stencil.remove(); + this.scroll.drawScrollbar(); + } + } + + public int getScrollY() { + return this.scroll.getScrollY() != null ? this.scroll.getScrollY().getScroll() : 0; + } + + @Override + public IRichTextBuilder getRichText() { + return this.text; + } + + /** + * Sets the auto update property. If auto update is true the text will be deleted each time it is drawn. + * If {@link #builder} is not null, it will then be called. + * + * @param autoUpdate auto update + * @return this + */ + public ScrollableTextWidget autoUpdate(boolean autoUpdate) { + this.autoUpdate = autoUpdate; + return this; + } + + /** + * A builder which is called every time before drawing when {@link #dirty} is true. + * + * @param builder text builder + * @return this + */ + public ScrollableTextWidget textBuilder(Consumer builder) { + this.builder = builder; + markDirty(); + return this; + } + + @Override + public @Nullable Object getIngredient() { + return this.lastIngredient; + } +} diff --git a/src/main/java/gregtech/mixins/mui2/AbstractCycleButtonWidgetMixin.java b/src/main/java/gregtech/mixins/mui2/AbstractCycleButtonWidgetMixin.java new file mode 100644 index 00000000000..5f72806e42e --- /dev/null +++ b/src/main/java/gregtech/mixins/mui2/AbstractCycleButtonWidgetMixin.java @@ -0,0 +1,52 @@ +package gregtech.mixins.mui2; + +import com.cleanroommc.modularui.api.ITheme; +import com.cleanroommc.modularui.api.drawable.IDrawable; +import com.cleanroommc.modularui.theme.WidgetTheme; +import com.cleanroommc.modularui.widget.Widget; +import com.cleanroommc.modularui.widgets.AbstractCycleButtonWidget; +import com.llamalad7.mixinextras.injector.ModifyReturnValue; +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 java.util.Arrays; + +@Mixin(value = AbstractCycleButtonWidget.class, remap = false) +public class AbstractCycleButtonWidgetMixin> extends Widget { + + @Shadow + protected IDrawable[] hoverBackground; + + @Shadow + protected IDrawable[] hoverOverlay; + + @ModifyReturnValue(method = "getCurrentOverlay", at = @At(value = "RETURN", ordinal = 0)) + public IDrawable fixOverlay(IDrawable original, + @Local(argsOnly = true) ITheme theme, + @Local(argsOnly = true) WidgetTheme widgetTheme) { + return original != IDrawable.NONE ? original : super.getCurrentOverlay(theme, widgetTheme); + } + + @ModifyReturnValue(method = "getCurrentBackground", at = @At(value = "RETURN", ordinal = 0)) + public IDrawable fixBackground(IDrawable original, + @Local(argsOnly = true) ITheme theme, + @Local(argsOnly = true) WidgetTheme widgetTheme) { + return original != IDrawable.NONE ? original : super.getCurrentBackground(theme, widgetTheme); + } + + @Override + public W disableHoverBackground() { + if (this.hoverBackground != null) + Arrays.fill(this.hoverBackground, IDrawable.NONE); + return getThis(); + } + + @Override + public W disableHoverOverlay() { + if (this.hoverOverlay != null) + Arrays.fill(this.hoverOverlay, IDrawable.NONE); + return getThis(); + } +} diff --git a/src/main/java/gregtech/mixins/mui2/IconMixin.java b/src/main/java/gregtech/mixins/mui2/IconMixin.java new file mode 100644 index 00000000000..faaafe69d34 --- /dev/null +++ b/src/main/java/gregtech/mixins/mui2/IconMixin.java @@ -0,0 +1,38 @@ +package gregtech.mixins.mui2; + +import gregtech.api.mui.IconAcessor; + +import com.cleanroommc.modularui.api.drawable.IDrawable; +import com.cleanroommc.modularui.api.drawable.IIcon; +import com.cleanroommc.modularui.drawable.DelegateIcon; +import com.cleanroommc.modularui.drawable.Icon; +import org.spongepowered.asm.mixin.Final; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.Shadow; + +@Mixin(value = Icon.class, remap = false) +public class IconMixin implements IconAcessor { + + @Shadow + @Final + private IDrawable drawable; + + public IDrawable gregTech$getDrawable() { + return this.drawable; + } + + @Mixin(value = DelegateIcon.class, remap = false) + public static abstract class DelegateMixin implements IconAcessor { + + @Shadow + public abstract IIcon getDelegate(); + + @Override + public IDrawable gregTech$getDrawable() { + if (getDelegate() instanceof IconAcessor acessor) { + return acessor.gregTech$getDrawable(); + } + return null; + } + } +} 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 raw, CallbackInfo ci, @Local IKey key) { + x += fr.getStringWidth(key.get()); + } +} diff --git a/src/main/resources/assets/gregtech/lang/en_us.lang b/src/main/resources/assets/gregtech/lang/en_us.lang index 3f0ed24b9d8..79b97d5b382 100644 --- a/src/main/resources/assets/gregtech/lang/en_us.lang +++ b/src/main/resources/assets/gregtech/lang/en_us.lang @@ -4887,13 +4887,8 @@ gregtech.machine.miner.multi.production=Produces §f3x§7 more crushed ore than gregtech.machine.miner.fluid_usage=Uses §f%,d L/t §7of §f%s§7, doubled per overclock. gregtech.machine.miner.multi.description=A multiblock mining machine that covers a large area and produces huge quantity of ore. gregtech.machine.miner.multi.needsfluid=No Drilling Fluid! - -gregtech.machine.miner.startx=sX: %d -gregtech.machine.miner.starty=sY: %d -gregtech.machine.miner.startz=sZ: %d -gregtech.machine.miner.minex=mX: %d -gregtech.machine.miner.miney=mY: %d -gregtech.machine.miner.minez=mZ: %d +gregtech.machine.miner.mining_at=Currently mining at: +gregtech.machine.miner.mining_pos=X: %s Y: %s Z: %s gregtech.machine.miner.radius=Radius: %d gregtech.machine.miner.working_area=Working Area: %dx%d blocks gregtech.machine.miner.working_area_chunks=Working Area: %dx%d chunks @@ -4968,7 +4963,7 @@ gregtech.machine.research_station.tooltip.2=Used to scan onto §fData Orbs§7 an gregtech.machine.research_station.tooltip.3=Requires §fComputation§7 to work. gregtech.machine.research_station.tooltip.4=Providing more Computation allows the recipe to run faster. gregtech.multiblock.research_station.description=The Research Station is a multiblock structure used for researching much more complex Assembly Line Research Data. Any Research requiring a Data Orb or Data Module must be scanned in the Research Station. Requires Compute Work Units (CWU/t) to research recipes, which is supplied by High Performance Computing Arrays (HPCAs). -gregtech.machine.research_station.researching=Researching. +gregtech.machine.research_station.researching=Researching: gregtech.machine.network_switch.name=Network Switch gregtech.machine.network_switch.tooltip.1=Ethernet Hub @@ -4985,13 +4980,14 @@ gregtech.multiblock.high_performance_computing_array.description=The High Perfor gregtech.machine.central_monitor.name=Central Monitor gregtech.multiblock.central_monitor.low_power=Low Power -gregtech.multiblock.central_monitor.height=Screen Height: +gregtech.multiblock.central_monitor.height=Screen Height: %d gregtech.multiblock.central_monitor.width=Screen Width: %d gregtech.multiblock.central_monitor.height_modify=Modify Height: %d gregtech.multiblock.central_monitor.tooltip.1=This is a machine that monitors machines proxied by the Digital Interface Cover. You can easily monitor the Fluids, Items, Energy, and States of machines proxied in Energy Network. gregtech.multiblock.central_monitor.tooltip.2=You can build the central monitor screen from 3X2 to %dX%d (width X height). gregtech.multiblock.central_monitor.tooltip.3=The default height is 3. You can adjust the screen height in the GUI before the structure is formed. gregtech.multiblock.central_monitor.tooltip.4=Energy consumption: %d EU/s for each screen. +gregtech.multiblock.central_monitor.button_tooltip=Left click to increase height, right click to decrease height. Middle click to reset gregtech.multiblock.monitor_screen.tooltip.1=The GUI can be opened with a right-click of a screwdriver. gregtech.multiblock.monitor_screen.tooltip.2=The proxy mode of Digital Interface Cover can delegate machines' capabilities and GUI. (Yes, you can connect pipes directly on the screen.) gregtech.multiblock.monitor_screen.tooltip.3=The screen also supports plugins. @@ -5660,11 +5656,12 @@ gregtech.gui.fluid_voiding.tooltip.enabled=Excess Fluid Voiding Enabled gregtech.gui.fluid_voiding.tooltip.disabled=Excess Fluid Voiding Disabled gregtech.gui.item_voiding.tooltip.enabled=Excess Item Voiding Enabled gregtech.gui.item_voiding.tooltip.disabled=Excess Item Voiding Disabled -gregtech.gui.multiblock_item_voiding=Voiding Mode/n§7Voiding §6Items -gregtech.gui.multiblock_fluid_voiding=Voiding Mode/n§7Voiding §9Fluids -gregtech.gui.multiblock_item_fluid_voiding=Voiding Mode/n§7Voiding §6Items §7and §9Fluids -gregtech.gui.multiblock_no_voiding=Voiding Mode/n§7Voiding Nothing +gregtech.gui.multiblock_item_voiding=Voiding Mode\n§7Voiding §6Items +gregtech.gui.multiblock_fluid_voiding=Voiding Mode\n§7Voiding §9Fluids +gregtech.gui.multiblock_item_fluid_voiding=Voiding Mode\n§7Voiding §6Items §7and §9Fluids +gregtech.gui.multiblock_no_voiding=Voiding Mode\n§7Voiding Nothing gregtech.gui.multiblock_voiding_not_supported=This Multiblock does not support Voiding Mode +gregtech.gui.multiblock.recipe_producing=Producing: gregtech.gui.me_network.online=Network: §aOnline§r gregtech.gui.me_network.offline=Network: §4Offline§r gregtech.gui.waiting_list=Sending Queue: @@ -5799,7 +5796,9 @@ gregtech.multiblock.idling=Idling. gregtech.multiblock.not_enough_energy=Machine needs more energy! gregtech.multiblock.not_enough_energy_output=Energy Dynamo Tier Too Low! gregtech.multiblock.progress=Progress: %s%% -gregtech.multiblock.invalid_structure=Invalid structure. +gregtech.multiblock.recipe_progress.seconds=Progress: %,3.2fs / %,3.2fs (%,3.1f%%) +gregtech.multiblock.recipe_progress.ticks=Progress: %st / %st (%,3.1f%%) +gregtech.multiblock.invalid_structure=§cInvalid structure. gregtech.multiblock.invalid_structure.tooltip=This block is a controller of the multiblock structure. For building help, see structure template in JEI. gregtech.multiblock.validation_failed=Invalid amount of inputs/outputs. gregtech.multiblock.max_energy_per_tick=Max EU/t: %s (%s) @@ -5819,16 +5818,17 @@ gregtech.multiblock.universal.problem.wire_cutter=Wires burned out. (Wire Cutter gregtech.multiblock.universal.problem.crowbar=That doesn't belong there. (Crowbar) gregtech.multiblock.universal.muffler_obstructed=Muffler Hatch is Obstructed! gregtech.multiblock.universal.muffler_obstructed_desc=Muffler Hatch must have a block of airspace in front of it. -gregtech.multiblock.universal.distinct_enabled=Distinct Buses: §aEnabled§r/nEach Item Input Bus will be treated separately for recipe lookup. Useful for things like Programmed Circuits, Extruder Shapes, etc. -gregtech.multiblock.universal.distinct_disabled=Distinct Buses: §cDisabled§r/nEach Item Input Bus will be treated as a combined input for recipe lookup. +gregtech.multiblock.universal.distinct_enabled=Distinct Buses: §aEnabled§r\nEach Item Input Bus will be treated separately for recipe lookup. Useful for things like Programmed Circuits, Extruder Shapes, etc. +gregtech.multiblock.universal.distinct_disabled=Distinct Buses: §cDisabled§r\nEach Item Input Bus will be treated as a combined input for recipe lookup. gregtech.multiblock.universal.distinct_not_supported=This Multiblock does not support Distinct Buses gregtech.multiblock.universal.no_flex_button=This Multiblock has no additional functionality with this Button. gregtech.multiblock.parallel=Max Parallels: %s gregtech.multiblock.multiple_recipemaps.header=Machine Mode: +gregtech.multiblock.multiple_recipemaps.value=Machine Mode: %s gregtech.multiblock.multiple_recipemaps.tooltip=Screwdriver the controller to change which machine mode to use. gregtech.multiblock.multiple_recipemaps_recipes.tooltip=Machine Modes: §e%s§r gregtech.multiblock.multiple_recipemaps.switch_message=The machine must be off to switch modes! -gregtech.multiblock.energy_stored=Energy: %s +gregtech.multiblock.energy_stored=§7Energy: §e%,d / %,d EU gregtech.multiblock.preview.zoom=Use mousewheel or right-click + drag to zoom gregtech.multiblock.preview.rotate=Click and drag to rotate @@ -5858,27 +5858,30 @@ gregtech.multiblock.multi_furnace.heating_coil_discount_hover=Energy usage modif gregtech.multiblock.multi_furnace.parallel_hover=Multi Smelter Parallels, determined by coil tier gregtech.multiblock.distillation_tower.distilling_fluid=Distilling %s -gregtech.multiblock.large_combustion_engine.fuel_amount=Fuel: %s -gregtech.multiblock.large_combustion_engine.no_lubricant=No Lubricant! -gregtech.multiblock.large_combustion_engine.lubricant_amount=Lubricant: %s -gregtech.multiblock.large_combustion_engine.oxygen_amount=Oxygen: %s -gregtech.multiblock.large_combustion_engine.liquid_oxygen_amount=Liquid Oxygen: %s +gregtech.multiblock.large_combustion_engine.fuel_none=§cNo Fuel! +gregtech.multiblock.large_combustion_engine.fuel_amount=§7Fuel: %,d / %,d L (§6%s§7) +gregtech.multiblock.large_combustion_engine.no_lubricant=§cNo Lubricant! +gregtech.multiblock.large_combustion_engine.lubricant_amount=§7Lubricant: §6%,d / %,d L +gregtech.multiblock.large_combustion_engine.oxygen_none=§cNo Booster! +gregtech.multiblock.large_combustion_engine.oxygen_amount=Oxygen: §b%,d / %,d L +gregtech.multiblock.large_combustion_engine.liquid_oxygen_amount=Liquid Oxygen: §b%,d / %,d L gregtech.multiblock.large_combustion_engine.oxygen_boosted=Oxygen boosted. gregtech.multiblock.large_combustion_engine.liquid_oxygen_boosted=Liquid Oxygen boosted. -gregtech.multiblock.large_combustion_engine.oxygen_boost_disallowed=Upgrade the Dynamo Hatch to enable Oxygen Boosting. -gregtech.multiblock.large_combustion_engine.liquid_oxygen_boost_disallowed=Upgrade the Dynamo Hatch to enable Liquid Oxygen Boosting. +gregtech.multiblock.large_combustion_engine.oxygen_boost_disallowed=§eUpgrade the Dynamo Hatch to enable Oxygen Boosting. +gregtech.multiblock.large_combustion_engine.liquid_oxygen_boost_disallowed=§eUpgrade the Dynamo Hatch to enable Liquid Oxygen Boosting. gregtech.multiblock.large_combustion_engine.supply_oxygen_to_boost=Supply Oxygen to boost. gregtech.multiblock.large_combustion_engine.supply_liquid_oxygen_to_boost=Supply Liquid Oxygen to boost. +gregtech.multiblock.large_combustion_engine.dynamo_hatch_full=Dynamo Hatch is Full! gregtech.multiblock.large_combustion_engine.obstructed=Engine Intakes Obstructed! gregtech.multiblock.large_combustion_engine.obstructed.desc=Engine Intakes must have a block of airspace in front of them. gregtech.multiblock.turbine.fuel_amount=Fuel: %sL %s gregtech.multiblock.turbine.fuel_needed=Consumes %s per %s ticks -gregtech.multiblock.turbine.rotor_speed=Rotor Speed: %s -gregtech.multiblock.turbine.rotor_rpm_unit_name=RPM -gregtech.multiblock.turbine.rotor_durability=Rotor Durability: %s -gregtech.multiblock.turbine.rotor_durability_low=Rotor durability low! -gregtech.multiblock.turbine.no_rotor=No Rotor in Rotor Holder! +gregtech.multiblock.turbine.rotor_speed=§7Rotor Speed: %s%,d / %,d RPM +gregtech.multiblock.turbine.rotor_durability.high=§7Rotor Durability: §a%,d%% +gregtech.multiblock.turbine.rotor_durability.medium=§7Rotor Durability: §e%,d%% +gregtech.multiblock.turbine.rotor_durability.low=§cRotor durability low! +gregtech.multiblock.turbine.no_rotor=§eNo Rotor in Rotor Holder! gregtech.multiblock.turbine.efficiency=Turbine Efficiency: %s gregtech.multiblock.turbine.energy_per_tick=Output EU/t: %s (%s) gregtech.multiblock.turbine.obstructed=Turbine Face Obstructed! @@ -5887,15 +5890,17 @@ gregtech.multiblock.turbine.efficiency_tooltip=Each Rotor Holder above %s§7 add gregtech.multiblock.large_boiler.efficiency=Efficiency: %s gregtech.multiblock.large_boiler.steam_output=Steam Output: %s +gregtech.multiblock.large_boiler.throttle_button.tooltip=Configure Boiler Throttle gregtech.multiblock.large_boiler.throttle=Throttle: %s +gregtech.multiblock.large_boiler.throttle.title=Boiler Throttle gregtech.multiblock.large_boiler.throttle.tooltip=Boiler can output less Steam and consume less fuel (efficiency is not lost, does not affect heat-up time) gregtech.multiblock.large_boiler.throttle_increment=Increase throttle by §a+5%%§r/n§7Boiler can output less Steam and consume less fuel (efficiency is not lost, does not affect heat-up time) gregtech.multiblock.large_boiler.throttle_decrement=Decrease throttle by §c-5%%§r/n§7Boiler can output less Steam and consume less fuel (efficiency is not lost, does not affect heat-up time) gregtech.multiblock.large_boiler.rate_tooltip=§7Produces §f%d L §7of Steam with §f1 Coal gregtech.multiblock.large_boiler.heat_time_tooltip=§7Takes §f%,d seconds §7to heat up gregtech.multiblock.large_boiler.explosion_tooltip=Will explode if provided Fuel with no Water -gregtech.multiblock.large_boiler.water_bar_hover=Water: %s -gregtech.multiblock.large_boiler.no_water=No Water! +gregtech.multiblock.large_boiler.water_bar_hover=§7Water: §9%,d / %,d L +gregtech.multiblock.large_boiler.no_water=§eNo Water! gregtech.machine.miner.done=Done! gregtech.machine.miner.working=Working... @@ -5913,8 +5918,10 @@ gregtech.multiblock.miner.both_modes=Chunk Mode: §aEnabled§r/nSilk Touch Mode: gregtech.multiblock.fluid_rig.drilled_fluid=Fluid: %s gregtech.multiblock.fluid_rig.no_fluid_in_area=None in Area. gregtech.multiblock.fluid_rig.fluid_amount=Pumping Rate: %s -gregtech.multiblock.fluid_rig.vein_depletion=Vein Size: %s -gregtech.multiblock.fluid_rig.vein_depleted=Vein Depleted. +gregtech.multiblock.fluid_rig.vein_depletion.high=§7Vein Size: §a%,d%% +gregtech.multiblock.fluid_rig.vein_depletion.medium=§7Vein Size: §e%,d%% +gregtech.multiblock.fluid_rig.vein_depletion.low=§7Vein Size: §c%,d%% +gregtech.multiblock.fluid_rig.vein_depleted=§cVein Depleted. gregtech.multiblock.miner.drilling=Drilling. gregtech.multiblock.pyrolyse_oven.speed=Processing Speed: %s @@ -5949,9 +5956,9 @@ gregtech.multiblock.power_substation.under_one_hour_left=Less than 1 hour until gregtech.multiblock.data_bank.providing=Providing data. -gregtech.multiblock.hpca.computation=Providing: %s +gregtech.multiblock.hpca.computation=§7Providing: §b%,d / %,d CWU/t gregtech.multiblock.hpca.energy=Using: %s / %s EU/t (%s) -gregtech.multiblock.hpca.temperature=Temperature: %s +gregtech.multiblock.hpca.temperature=Temperature: %,d°C gregtech.multiblock.hpca.hover_for_info=Hover for details gregtech.multiblock.hpca.error_damaged=Damaged component in structure! gregtech.multiblock.hpca.error_temperature=Temperature above 100C, components may be damaged! @@ -5969,6 +5976,8 @@ gregtech.multiblock.hpca.info_coolant_name=PCB Coolant gregtech.multiblock.hpca.info_bridging_enabled=Bridging Enabled gregtech.multiblock.hpca.info_bridging_disabled=Bridging Disabled +gregtech.multiblock.machine_mode=Machine Mode: %s + gregtech.command.usage=Usage: /gregtech gregtech.command.worldgen.usage=Usage: /gregtech worldgen gregtech.command.worldgen.reload.usage=Usage: /gregtech worldgen reload diff --git a/src/main/resources/assets/gregtech/textures/gui/overlay/filter_settings_overlay.png b/src/main/resources/assets/gregtech/textures/gui/overlay/filter_settings_overlay.png index 31b8cdb6ce8..8ce5b77a856 100644 Binary files a/src/main/resources/assets/gregtech/textures/gui/overlay/filter_settings_overlay.png and b/src/main/resources/assets/gregtech/textures/gui/overlay/filter_settings_overlay.png differ diff --git a/src/main/resources/assets/gregtech/textures/gui/progress_bar/fusion_diagram/stitched.png b/src/main/resources/assets/gregtech/textures/gui/progress_bar/fusion_diagram/stitched.png new file mode 100644 index 00000000000..8ba66f3caca Binary files /dev/null and b/src/main/resources/assets/gregtech/textures/gui/progress_bar/fusion_diagram/stitched.png differ diff --git a/src/main/resources/assets/gregtech/textures/gui/widget/button_distinct_buses.png b/src/main/resources/assets/gregtech/textures/gui/widget/button_distinct_buses.png index 9db435b36bc..4ab3c6f5c9a 100644 Binary files a/src/main/resources/assets/gregtech/textures/gui/widget/button_distinct_buses.png and b/src/main/resources/assets/gregtech/textures/gui/widget/button_distinct_buses.png differ diff --git a/src/main/resources/assets/gregtech/textures/gui/widget/button_no_distinct_buses.png b/src/main/resources/assets/gregtech/textures/gui/widget/button_no_distinct_buses.png deleted file mode 100644 index fd5cb681e07..00000000000 Binary files a/src/main/resources/assets/gregtech/textures/gui/widget/button_no_distinct_buses.png and /dev/null differ diff --git a/src/main/resources/assets/gregtech/textures/gui/widget/button_no_flex.png b/src/main/resources/assets/gregtech/textures/gui/widget/button_no_flex.png index b2c4bcbcd86..fd1ae436da9 100644 Binary files a/src/main/resources/assets/gregtech/textures/gui/widget/button_no_flex.png and b/src/main/resources/assets/gregtech/textures/gui/widget/button_no_flex.png differ diff --git a/src/main/resources/assets/gregtech/textures/gui/widget/button_power.png b/src/main/resources/assets/gregtech/textures/gui/widget/button_power.png index 325c064669f..64d71a2ee00 100644 Binary files a/src/main/resources/assets/gregtech/textures/gui/widget/button_power.png and b/src/main/resources/assets/gregtech/textures/gui/widget/button_power.png differ diff --git a/src/main/resources/assets/gregtech/textures/gui/widget/button_void_multiblock.png b/src/main/resources/assets/gregtech/textures/gui/widget/button_void_multiblock.png index f2133f16ec8..3b8353e371d 100644 Binary files a/src/main/resources/assets/gregtech/textures/gui/widget/button_void_multiblock.png and b/src/main/resources/assets/gregtech/textures/gui/widget/button_void_multiblock.png differ diff --git a/src/main/resources/assets/gregtech/textures/gui/widget/button_void_none.png b/src/main/resources/assets/gregtech/textures/gui/widget/button_void_none.png index c4c436aa1a8..cf30bdd5357 100644 Binary files a/src/main/resources/assets/gregtech/textures/gui/widget/button_void_none.png and b/src/main/resources/assets/gregtech/textures/gui/widget/button_void_none.png differ diff --git a/src/main/resources/assets/gregtech/textures/gui/widget/buttons.png b/src/main/resources/assets/gregtech/textures/gui/widget/buttons.png new file mode 100644 index 00000000000..6035435b34c Binary files /dev/null and b/src/main/resources/assets/gregtech/textures/gui/widget/buttons.png differ diff --git a/src/main/resources/mixins.gregtech.mui2.json b/src/main/resources/mixins.gregtech.mui2.json index f313fa2b32d..10684de90fb 100644 --- a/src/main/resources/mixins.gregtech.mui2.json +++ b/src/main/resources/mixins.gregtech.mui2.json @@ -8,7 +8,11 @@ "maxShiftBy": 10 }, "mixins": [ - "ModularPanelMixin" + "AbstractCycleButtonWidgetMixin", + "IconMixin", + "IconMixin$DelegateMixin", + "ModularPanelMixin", + "RichTextCompilerMixin" ], "client": [ "LangKeyMixin" diff --git a/src/test/java/gregtech/common/metatileentities/multiblock/hpca/helper/HPCAComponentHatchTestImpl.java b/src/test/java/gregtech/common/metatileentities/multiblock/hpca/helper/HPCAComponentHatchTestImpl.java index 47d6dbf742a..48bbad02219 100644 --- a/src/test/java/gregtech/common/metatileentities/multiblock/hpca/helper/HPCAComponentHatchTestImpl.java +++ b/src/test/java/gregtech/common/metatileentities/multiblock/hpca/helper/HPCAComponentHatchTestImpl.java @@ -1,7 +1,6 @@ package gregtech.common.metatileentities.multiblock.hpca.helper; import gregtech.api.capability.IHPCAComponentHatch; -import gregtech.api.gui.resources.TextureArea; public class HPCAComponentHatchTestImpl implements IHPCAComponentHatch { @@ -34,10 +33,4 @@ public boolean canBeDamaged() { public boolean isBridge() { return false; } - - // not tested - @Override - public TextureArea getComponentIcon() { - return null; - } }