From b65ace37de4f585b8089ad413ee877b792da11ca Mon Sep 17 00:00:00 2001 From: NotAPenguin Date: Sat, 3 Aug 2024 23:21:44 +0200 Subject: Waterline rework (#2577) * Add barebones PurificationPlant class * Make simple 3x3 structure to form purification plant * Add base purification unit class and dummy sifter unit MTE * Make sifter unit form * Fix accidental wildcard import * Implement basic linking of units to controller using data stick * Make linking more robust, save bidirectional links, add scanner output * add linking range, error message and unregister old controller when re-linking * Add link status of purification units to waila body * Disable maintenance issues on purification plant units * spotless * Check structure of linked purification units in main controller * Remove all star imports * Small refactor to avoid updating status from main controller * spotless * Attempt to document current code * Convert some comments to javadoc * Implement basic processing cycle, sync it with linked purification units * Make water purification plant drain power * Calculate power drain from active units and remove controller power drain * spotless * Add very barebones recipemap * Fix recipemap name in lang file * spotless * Fix purification unit recipemap name * spotless * more sane amount of max fluid outputs * add some item outputs to sifter unit * Very simple recipe processing, may be buggy * spotless * Implement recipe failure * Implement void protection for purification units * spotless * buff item output odds slightly * Add WIP grade 1 structure * spotless * Store base success chance in recipe metadata and display it in NEI * Fill sifter plant with water * Add comment * Allow construction sifter unit in survival * Implement water boost * Fix water boost allowing output chance to go over 100% * Implement failed recipes outputting lower tier water * Fix typo * Fix deformed purification unit still drawing power * Slightly refactor recipe check so base class can read result * Create empty ModularUI container for purification plant * The great gui struggle part 1 * More gui struggles, we have a button now * Adjust button text and size * gui wars: the rise of the sync * gui wars: a new hope * fix the sync * is pengu old enough to know exceeder? * Fix being able to link the same unit multiple times * Sync status string to client * Sign sifter with my name * Show status somewhat properly * Adjust sifter base chance and structure * Fully implement sifter unit * More tooltip refactoring * Add structure info to sifter tooltip. * nitpicking tooltips * Adding sound to Purification Plant Main Unit. * fix star imports * Add basic coagulator unit, add recipemap for it * Write coagulator tooltip * comma nitpicking * more tooltip work * small refactor to purification plant controller * start work on custom recipemap frontend * Fully implement coagulator * Update structure requirements in tooltips * Move controller text in structure tooltips to be consistent * fix NPE on world load * Add base ph adjustment unit MTE * Add info to main controller and energy hatch check * Fixing tooltip of Main Controller & Energy/Exotic Hatch check. * Create full pH adjustment structure * disallow any voiding on purification unit * Small custom RecipeMap frontend for ph adjustment * Generate random initial pH value * Implement inserting NaOH and HCl to adjust pH * Add easter egg * Implement pH sensor hatch * Properly consume HCl and round pH value to 2 digits * Write ph adjustment unit tooltip * Tooltip nitpicking * Try to fix some structurelib hints * More trying to fix hints * Add industrial strength concrete casing block * Add water loop sound to the game * Document random initial pH in tooltip * Add glass material base * Fix spotless formatting in Textures docs because I cannot take it anymore * Add glass texture * Try adding transparent glass * Transparent glass working * Create pH resistant glass and update pH structure to use it * Create full structure for main purification plant * Create custom water purification casing block * Properly balance ferrous wastewater reprocessing and reduce input by a factor 10 * Add pH factor to NEI tooltip and fix coagulator structure * Structure tooltip for Purification Plant base * Add GT_Block_Glass2 and properly set maxMeta * Add Tinted Industrial Glass blocks * Fix BlockCasing9 not showing custom tooltip * Register tinted glass as EV glass * Add sterile water plant casing and revert tooltip change * Mention required water in sifter tooltip * Add more textures and casings * Add more textures, sounds and add structure info for pH adjustment * Rename sifter unit to clarifier * Rename coagulation unit to flocculation unit * Add activated carbon line * Fix unintended activated carbon recipe * Add activated carbon filter * Add polyaluminium chloride + solution * Add new custom textures by @BlueHero233 * Wip recipe page with new background for flocculation * Fix flocculation background image mostly * Finally aligned the slots * angery mumbles * Finish flocculation recipe page * All the recipe pages! * Add new reworked textures * Fix ph adjustment being t3 instead of t4 * Fix invisible casing * apply chembalance to polyaluminium chloride properly * Fix ferrous wastewater -> flocculation waste liquid * Move flocculation to grade 3 * create ozonation unit with placeholder blocks * add new blocks for ozonation with placeholder textures * Add water to ozonation structure * Create ozone gas material * Add ozone recipe * Add textures for ozone unit * Add sound loop for ozonation * fix * implement ozonation mechanics * Finalize ozonation tooltip * Create dummy plasma heater multi * Update textures for plasma heater * Add grade 5 recipemap * Add hatches to plasma heater multi * Add basic plasma heating unit variables * Implement plasma heating unit mechanics * Add plasma heater tooltip * Add structure info to plasma heater tooltip * fix ozonation tooltip, add frontend * Fix positioning on ozonation tooltip and fix plasma heater crash * Add UV treatment MTE and structure without textures * Revert accidental addition of debug dependencies * Add initial version of uv unit textures * update naquadria casing, add water color gradient * Some minor cleanup and added docs * Create lens housing bus * Add lens bus to UV treatment unit * Add lens indicator hatch * Merge GT_MetaGeneratedItem_03.java * Add lens indicator hatch * Add the lens cycle, uv treatment recipe map and fix eut of flocculation recipe * Implement lens swapping mechanic * Clean up first lens swap * Fix uv recipemap lang and move lens cycle to recipe * Write uv treatment tooltip * Add sounds for uv and plasma steps * Create empty degasifier class * Create temporary debug structure for degasifier * set temp casing index for degasifier * create degasifier control hatch * create slightly less temporary but still temporary structure for degasifier * Start impl of degasifier * fix fluid consumption and nbt loading of degasifier * Degasifier implementation should work now * Rename and reformat some things and start work on degasser tooltiop * give last bit much lower chance of being on to avoid cheesing * Finish degasifier tooltip * Integrate some deleno lore * hopefully fix all moved casing meta ids after merge * Create finalized degasser structure * Integrate more deleno lore * Add even more lore * Create placeholder particle catalysts and fetch particle items from gt++ * Fix wrong casing and recipemap localization * Create parallel config menu * refactor purification recipecheck slightly * implement parallel amount on water i/o and power * add tooltip info about parallel config * fix text * update block names in structure tooltips * create structure tooltip for degasser * create textureless quark catalyst items * add the purple glass * fix lore typos * fix some casing indices * remove concrete floor from water plant and reword tooltip * fix main plant structure and add placeholder structure for t8 step * fix structurecheck for main plant and add random catalyst generation for t8 * implement basic mechanics for particle extractor (wip) * Create plasma heater frontend * implement final mechanics and bugfixes for particle extractor * add recipes for re-aligning quark catalysts * add simple recipes for catalyst alignment * initial replacement of purified water in engraver recipes * add purified water to all wafer cutting recipes * fix purified water amounts * buff quark cyclotron recipe again * extract t8 unit casings into their own icons * Write initial tooltip for t8 module * add purified water to mask recipes * Add recipe comparator to show low tier purified water recipes first * add min casing check to waterline multis * buff ozone production * update t8 structure * make purified water optional again for naq wafers * Fix blockrenderer for purification plant * fix nei previews * fix nei * really fix nei this time * add t8 lore * fix hatch recipe locking blocking automation on some steps * try to solve weirdness with grade 3 recipe * fix issues with recipecheck * fix missing null check * make ph sensor use a strict inequality check * fix min casings on t5 * significantly nerf purified water usage for beamline masks * disable void protection for waterline * small adjustments to t6 unit * more small adjustments to t6 unit to prevent easy automation cheese * fix degasser redstone output and missing return statement * remove water QFT catalyst recipes --------- Co-authored-by: Tianyou Mei Co-authored-by: OlliedeLeeuw Co-authored-by: Ollie_de_Leeuw <154506304+OlliedeLeeuw@users.noreply.github.com> Co-authored-by: Martin Robertz --- ...etaTileEntity_Hatch_DegasifierControlHatch.java | 122 +++ .../GT_MetaTileEntity_LensHousing.java | 52 ++ .../GT_MetaTileEntity_LensIndicator.java | 117 +++ .../GT_MetaTileEntity_PurificationPlant.java | 740 ++++++++++++++++++ .../GT_MetaTileEntity_PurificationUnitBase.java | 793 +++++++++++++++++++ ...T_MetaTileEntity_PurificationUnitClarifier.java | 333 ++++++++ ..._MetaTileEntity_PurificationUnitDegasifier.java | 838 +++++++++++++++++++++ ...etaTileEntity_PurificationUnitFlocculation.java | 496 ++++++++++++ ...T_MetaTileEntity_PurificationUnitOzonation.java | 299 ++++++++ ...leEntity_PurificationUnitParticleExtractor.java | 484 ++++++++++++ ...etaTileEntity_PurificationUnitPhAdjustment.java | 609 +++++++++++++++ ...etaTileEntity_PurificationUnitPlasmaHeater.java | 570 ++++++++++++++ ...MetaTileEntity_PurificationUnitUVTreatment.java | 524 +++++++++++++ .../purification/GT_MetaTileEntity_pHSensor.java | 196 +++++ .../multi/purification/LinkedPurificationUnit.java | 125 +++ .../PurificationPlantStructureString.java | 15 + .../multi/purification/PurificationUnitStatus.java | 14 + .../multi/purification/PurifiedWaterHelpers.java | 35 + .../multi/purification/UVTreatmentLensCycle.java | 39 + 19 files changed, 6401 insertions(+) create mode 100644 src/main/java/gregtech/common/tileentities/machines/multi/purification/GT_MetaTileEntity_Hatch_DegasifierControlHatch.java create mode 100644 src/main/java/gregtech/common/tileentities/machines/multi/purification/GT_MetaTileEntity_LensHousing.java create mode 100644 src/main/java/gregtech/common/tileentities/machines/multi/purification/GT_MetaTileEntity_LensIndicator.java create mode 100644 src/main/java/gregtech/common/tileentities/machines/multi/purification/GT_MetaTileEntity_PurificationPlant.java create mode 100644 src/main/java/gregtech/common/tileentities/machines/multi/purification/GT_MetaTileEntity_PurificationUnitBase.java create mode 100644 src/main/java/gregtech/common/tileentities/machines/multi/purification/GT_MetaTileEntity_PurificationUnitClarifier.java create mode 100644 src/main/java/gregtech/common/tileentities/machines/multi/purification/GT_MetaTileEntity_PurificationUnitDegasifier.java create mode 100644 src/main/java/gregtech/common/tileentities/machines/multi/purification/GT_MetaTileEntity_PurificationUnitFlocculation.java create mode 100644 src/main/java/gregtech/common/tileentities/machines/multi/purification/GT_MetaTileEntity_PurificationUnitOzonation.java create mode 100644 src/main/java/gregtech/common/tileentities/machines/multi/purification/GT_MetaTileEntity_PurificationUnitParticleExtractor.java create mode 100644 src/main/java/gregtech/common/tileentities/machines/multi/purification/GT_MetaTileEntity_PurificationUnitPhAdjustment.java create mode 100644 src/main/java/gregtech/common/tileentities/machines/multi/purification/GT_MetaTileEntity_PurificationUnitPlasmaHeater.java create mode 100644 src/main/java/gregtech/common/tileentities/machines/multi/purification/GT_MetaTileEntity_PurificationUnitUVTreatment.java create mode 100644 src/main/java/gregtech/common/tileentities/machines/multi/purification/GT_MetaTileEntity_pHSensor.java create mode 100644 src/main/java/gregtech/common/tileentities/machines/multi/purification/LinkedPurificationUnit.java create mode 100644 src/main/java/gregtech/common/tileentities/machines/multi/purification/PurificationPlantStructureString.java create mode 100644 src/main/java/gregtech/common/tileentities/machines/multi/purification/PurificationUnitStatus.java create mode 100644 src/main/java/gregtech/common/tileentities/machines/multi/purification/PurifiedWaterHelpers.java create mode 100644 src/main/java/gregtech/common/tileentities/machines/multi/purification/UVTreatmentLensCycle.java (limited to 'src/main/java/gregtech/common/tileentities/machines/multi') diff --git a/src/main/java/gregtech/common/tileentities/machines/multi/purification/GT_MetaTileEntity_Hatch_DegasifierControlHatch.java b/src/main/java/gregtech/common/tileentities/machines/multi/purification/GT_MetaTileEntity_Hatch_DegasifierControlHatch.java new file mode 100644 index 0000000000..3d49f05dca --- /dev/null +++ b/src/main/java/gregtech/common/tileentities/machines/multi/purification/GT_MetaTileEntity_Hatch_DegasifierControlHatch.java @@ -0,0 +1,122 @@ +package gregtech.common.tileentities.machines.multi.purification; + +import net.minecraft.item.ItemStack; +import net.minecraft.nbt.NBTTagCompound; +import net.minecraftforge.common.util.ForgeDirection; + +import gregtech.api.enums.Textures; +import gregtech.api.interfaces.IIconContainer; +import gregtech.api.interfaces.ITexture; +import gregtech.api.interfaces.metatileentity.IMetaTileEntity; +import gregtech.api.interfaces.tileentity.IGregTechTileEntity; +import gregtech.api.metatileentity.implementations.GT_MetaTileEntity_Hatch; +import gregtech.api.render.TextureFactory; + +public class GT_MetaTileEntity_Hatch_DegasifierControlHatch extends GT_MetaTileEntity_Hatch { + + private byte outputStrength = 0; + + private static final IIconContainer textureFont = Textures.BlockIcons.OVERLAY_HATCH_PH_SENSOR; + private static final IIconContainer textureFont_Glow = Textures.BlockIcons.OVERLAY_HATCH_PH_SENSOR_GLOW; + + public GT_MetaTileEntity_Hatch_DegasifierControlHatch(int aID, String aName, String aNameRegional, int aTier) { + super(aID, aName, aNameRegional, aTier, 0, "Outputs a control signal for the Degasser Purification Unit"); + } + + public GT_MetaTileEntity_Hatch_DegasifierControlHatch(String aName, int aTier, String[] aDescription, + ITexture[][][] aTextures) { + super(aName, aTier, 0, aDescription, aTextures); + } + + @Override + public boolean isValidSlot(int aIndex) { + return false; + } + + @Override + public boolean isSimpleMachine() { + return true; + } + + @Override + public boolean allowGeneralRedstoneOutput() { + return true; + } + + @Override + public boolean allowPullStack(IGregTechTileEntity aBaseMetaTileEntity, int aIndex, ForgeDirection Side, + ItemStack aStack) { + return false; + } + + @Override + public boolean allowPutStack(IGregTechTileEntity aBaseMetaTileEntity, int aIndex, ForgeDirection side, + ItemStack aStack) { + return false; + } + + @Override + public boolean isFacingValid(ForgeDirection facing) { + return true; + } + + @Override + public void initDefaultModes(NBTTagCompound aNBT) { + getBaseMetaTileEntity().setActive(true); + } + + @Override + public IMetaTileEntity newMetaEntity(IGregTechTileEntity aTileEntity) { + return new GT_MetaTileEntity_Hatch_DegasifierControlHatch(mName, mTier, mDescriptionArray, mTextures); + } + + @Override + public String[] getDescription() { + return new String[] { "Can be installed in the Degasser Purification Unit.", + "Outputs Redstone Signal Strength based on the current control signal." }; + } + + @Override + public void loadNBTData(NBTTagCompound aNBT) { + outputStrength = aNBT.getByte("mOutputStrength"); + super.loadNBTData(aNBT); + } + + @Override + public void saveNBTData(NBTTagCompound aNBT) { + aNBT.setByte("mOutputStrength", outputStrength); + super.saveNBTData(aNBT); + } + + // Pass zero signal to disable output + public void updateOutputSignal(byte signal) { + outputStrength = signal; + } + + @Override + public void onPostTick(IGregTechTileEntity aBaseMetaTileEntity, long aTick) { + if (outputStrength > 0) { + for (final ForgeDirection side : ForgeDirection.VALID_DIRECTIONS) { + aBaseMetaTileEntity.setStrongOutputRedstoneSignal(side, outputStrength); + } + } else { + for (final ForgeDirection side : ForgeDirection.VALID_DIRECTIONS) { + aBaseMetaTileEntity.setStrongOutputRedstoneSignal(side, (byte) 0); + } + } + super.onPostTick(aBaseMetaTileEntity, aTick); + } + + @Override + public ITexture[] getTexturesActive(ITexture aBaseTexture) { + return new ITexture[] { aBaseTexture, TextureFactory.of(textureFont), TextureFactory.builder() + .addIcon(textureFont_Glow) + .glow() + .build() }; + } + + @Override + public ITexture[] getTexturesInactive(ITexture aBaseTexture) { + return new ITexture[] { aBaseTexture, TextureFactory.of(textureFont) }; + } +} diff --git a/src/main/java/gregtech/common/tileentities/machines/multi/purification/GT_MetaTileEntity_LensHousing.java b/src/main/java/gregtech/common/tileentities/machines/multi/purification/GT_MetaTileEntity_LensHousing.java new file mode 100644 index 0000000000..bd025e0e69 --- /dev/null +++ b/src/main/java/gregtech/common/tileentities/machines/multi/purification/GT_MetaTileEntity_LensHousing.java @@ -0,0 +1,52 @@ +package gregtech.common.tileentities.machines.multi.purification; + +import com.gtnewhorizons.modularui.api.screen.ModularWindow; +import com.gtnewhorizons.modularui.api.screen.UIBuildContext; + +import gregtech.api.interfaces.ITexture; +import gregtech.api.interfaces.tileentity.IGregTechTileEntity; +import gregtech.api.metatileentity.MetaTileEntity; +import gregtech.api.metatileentity.implementations.GT_MetaTileEntity_Hatch_InputBus; +import gregtech.client.GT_TooltipHandler; + +public class GT_MetaTileEntity_LensHousing extends GT_MetaTileEntity_Hatch_InputBus { + + public GT_MetaTileEntity_LensHousing(int id, String name, String nameRegional) { + super( + id, + name, + nameRegional, + GT_TooltipHandler.Tier.UV.ordinal(), + 1, + new String[] { "Holds a lens for UV laser focusing." }); + } + + public GT_MetaTileEntity_LensHousing(String aName, int aTier, String[] aDescription, ITexture[][][] aTextures) { + super(aName, aTier, aDescription, aTextures); + } + + @Override + public MetaTileEntity newMetaEntity(IGregTechTileEntity aTileEntity) { + return new GT_MetaTileEntity_LensHousing(this.mName, this.mTier, this.mDescriptionArray, this.mTextures); + } + + @Override + public int getSizeInventory() { + return 1; + } + + @Override + public int getCircuitSlot() { + return -1; + } + + @Override + public boolean allowSelectCircuit() { + return false; + } + + @Override + public void addUIWidgets(ModularWindow.Builder builder, UIBuildContext buildContext) { + getBaseMetaTileEntity().add1by1Slot(builder); + } +} diff --git a/src/main/java/gregtech/common/tileentities/machines/multi/purification/GT_MetaTileEntity_LensIndicator.java b/src/main/java/gregtech/common/tileentities/machines/multi/purification/GT_MetaTileEntity_LensIndicator.java new file mode 100644 index 0000000000..c017025d42 --- /dev/null +++ b/src/main/java/gregtech/common/tileentities/machines/multi/purification/GT_MetaTileEntity_LensIndicator.java @@ -0,0 +1,117 @@ +package gregtech.common.tileentities.machines.multi.purification; + +import net.minecraft.entity.player.EntityPlayer; +import net.minecraft.item.ItemStack; +import net.minecraft.nbt.NBTTagCompound; +import net.minecraftforge.common.util.ForgeDirection; + +import gregtech.api.enums.Textures; +import gregtech.api.interfaces.IIconContainer; +import gregtech.api.interfaces.ITexture; +import gregtech.api.interfaces.metatileentity.IMetaTileEntity; +import gregtech.api.interfaces.tileentity.IGregTechTileEntity; +import gregtech.api.metatileentity.implementations.GT_MetaTileEntity_Hatch; +import gregtech.api.render.TextureFactory; + +public class GT_MetaTileEntity_LensIndicator extends GT_MetaTileEntity_Hatch { + + private boolean isOn = false; + + private static final IIconContainer textureFont = Textures.BlockIcons.OVERLAY_HATCH_PH_SENSOR; + private static final IIconContainer textureFont_Glow = Textures.BlockIcons.OVERLAY_HATCH_PH_SENSOR_GLOW; + + public GT_MetaTileEntity_LensIndicator(int aID, String aName, String aNameRegional, int aTier) { + super(aID, aName, aNameRegional, aTier, 0, "Indicates required lens swaps."); + } + + public GT_MetaTileEntity_LensIndicator(String aName, int aTier, String[] aDescription, ITexture[][][] aTextures) { + super(aName, aTier, 0, aDescription, aTextures); + } + + @Override + public boolean isValidSlot(int aIndex) { + return false; + } + + @Override + public boolean isSimpleMachine() { + return true; + } + + @Override + public boolean isFacingValid(ForgeDirection facing) { + return true; + } + + @Override + public boolean isAccessAllowed(EntityPlayer aPlayer) { + return true; + } + + @Override + public boolean allowGeneralRedstoneOutput() { + return true; + } + + @Override + public boolean allowPullStack(IGregTechTileEntity aBaseMetaTileEntity, int aIndex, ForgeDirection Side, + ItemStack aStack) { + return false; + } + + @Override + public boolean allowPutStack(IGregTechTileEntity aBaseMetaTileEntity, int aIndex, ForgeDirection side, + ItemStack aStack) { + return false; + } + + @Override + public void initDefaultModes(NBTTagCompound aNBT) { + getBaseMetaTileEntity().setActive(true); + } + + @Override + public String[] getDescription() { + return new String[] { "Can be installed in the UV Treatment Purification Unit.", + "Outputs Redstone Signal when a lens swap is requested." }; + } + + /** + * Updates redstone output strength based on the pH of the multiblock. + */ + public void updateRedstoneOutput(boolean enabled) { + isOn = enabled; + } + + @Override + public void onPostTick(IGregTechTileEntity aBaseMetaTileEntity, long aTick) { + if (isOn) { + for (final ForgeDirection side : ForgeDirection.VALID_DIRECTIONS) { + aBaseMetaTileEntity.setInternalOutputRedstoneSignal(side, (byte) 15); + } + } else { + for (final ForgeDirection side : ForgeDirection.VALID_DIRECTIONS) { + aBaseMetaTileEntity.setInternalOutputRedstoneSignal(side, (byte) 0); + } + } + super.onPostTick(aBaseMetaTileEntity, aTick); + } + + @Override + public IMetaTileEntity newMetaEntity(IGregTechTileEntity aTileEntity) { + return new GT_MetaTileEntity_LensIndicator(mName, mTier, mDescriptionArray, mTextures); + } + + @Override + public ITexture[] getTexturesActive(ITexture aBaseTexture) { + return new ITexture[] { aBaseTexture, TextureFactory.of(textureFont), TextureFactory.builder() + .addIcon(textureFont_Glow) + .glow() + .build() }; + } + + @Override + public ITexture[] getTexturesInactive(ITexture aBaseTexture) { + return new ITexture[] { aBaseTexture, TextureFactory.of(textureFont) }; + } +} diff --git a/src/main/java/gregtech/common/tileentities/machines/multi/purification/GT_MetaTileEntity_PurificationPlant.java b/src/main/java/gregtech/common/tileentities/machines/multi/purification/GT_MetaTileEntity_PurificationPlant.java new file mode 100644 index 0000000000..42bfd06d7a --- /dev/null +++ b/src/main/java/gregtech/common/tileentities/machines/multi/purification/GT_MetaTileEntity_PurificationPlant.java @@ -0,0 +1,740 @@ +package gregtech.common.tileentities.machines.multi.purification; + +import static com.gtnewhorizon.structurelib.structure.StructureUtility.lazy; +import static com.gtnewhorizon.structurelib.structure.StructureUtility.ofBlock; +import static com.gtnewhorizon.structurelib.structure.StructureUtility.ofBlockAnyMeta; +import static com.gtnewhorizon.structurelib.structure.StructureUtility.ofChain; +import static gregtech.api.enums.GT_HatchElement.Energy; +import static gregtech.api.enums.GT_HatchElement.ExoticEnergy; +import static gregtech.api.enums.GT_HatchElement.Maintenance; +import static gregtech.api.enums.GT_Values.AuthorNotAPenguin; +import static gregtech.api.enums.Textures.BlockIcons.OVERLAY_FRONT_PROCESSING_ARRAY; +import static gregtech.api.enums.Textures.BlockIcons.OVERLAY_FRONT_PROCESSING_ARRAY_ACTIVE; +import static gregtech.api.enums.Textures.BlockIcons.OVERLAY_FRONT_PROCESSING_ARRAY_ACTIVE_GLOW; +import static gregtech.api.enums.Textures.BlockIcons.OVERLAY_FRONT_PROCESSING_ARRAY_GLOW; +import static gregtech.api.util.GT_RecipeBuilder.SECONDS; +import static gregtech.api.util.GT_StructureUtility.ofFrame; +import static gregtech.common.tileentities.machines.multi.purification.GT_MetaTileEntity_PurificationUnitBase.WATER_BOOST_BONUS_CHANCE; +import static gregtech.common.tileentities.machines.multi.purification.GT_MetaTileEntity_PurificationUnitBase.WATER_BOOST_NEEDED_FLUID; + +import java.io.IOException; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; + +import net.minecraft.entity.player.EntityPlayer; +import net.minecraft.entity.player.EntityPlayerMP; +import net.minecraft.init.Blocks; +import net.minecraft.item.ItemStack; +import net.minecraft.nbt.NBTTagCompound; +import net.minecraft.util.ChatComponentText; +import net.minecraft.util.EnumChatFormatting; +import net.minecraftforge.common.util.ForgeDirection; + +import com.google.common.collect.ImmutableList; +import com.gtnewhorizon.structurelib.alignment.constructable.ISurvivalConstructable; +import com.gtnewhorizon.structurelib.structure.IStructureDefinition; +import com.gtnewhorizon.structurelib.structure.ISurvivalBuildEnvironment; +import com.gtnewhorizon.structurelib.structure.StructureDefinition; +import com.gtnewhorizons.modularui.api.drawable.shapes.Rectangle; +import com.gtnewhorizons.modularui.api.forge.ItemStackHandler; +import com.gtnewhorizons.modularui.api.math.Alignment; +import com.gtnewhorizons.modularui.api.math.Color; +import com.gtnewhorizons.modularui.api.math.Size; +import com.gtnewhorizons.modularui.api.screen.ModularWindow; +import com.gtnewhorizons.modularui.api.screen.UIBuildContext; +import com.gtnewhorizons.modularui.api.widget.Widget; +import com.gtnewhorizons.modularui.common.widget.ButtonWidget; +import com.gtnewhorizons.modularui.common.widget.DynamicPositionedColumn; +import com.gtnewhorizons.modularui.common.widget.DynamicPositionedRow; +import com.gtnewhorizons.modularui.common.widget.FakeSyncWidget; +import com.gtnewhorizons.modularui.common.widget.Scrollable; +import com.gtnewhorizons.modularui.common.widget.SlotWidget; +import com.gtnewhorizons.modularui.common.widget.TextWidget; + +import gregtech.api.GregTech_API; +import gregtech.api.enums.ItemList; +import gregtech.api.enums.Materials; +import gregtech.api.enums.Textures; +import gregtech.api.gui.modularui.GT_UITextures; +import gregtech.api.interfaces.IHatchElement; +import gregtech.api.interfaces.ITexture; +import gregtech.api.interfaces.metatileentity.IMetaTileEntity; +import gregtech.api.interfaces.tileentity.IGregTechTileEntity; +import gregtech.api.metatileentity.implementations.GT_MetaTileEntity_ExtendedPowerMultiBlockBase; +import gregtech.api.render.TextureFactory; +import gregtech.api.util.GT_Log; +import gregtech.api.util.GT_Multiblock_Tooltip_Builder; +import gregtech.api.util.GT_StructureUtility; +import gregtech.api.util.GT_Utility; +import gregtech.api.util.shutdown.ShutDownReasonRegistry; +import gregtech.common.gui.modularui.widget.ShutDownReasonSyncer; +import gregtech.common.gui.modularui.widget.TextButtonWidget; + +public class GT_MetaTileEntity_PurificationPlant + extends GT_MetaTileEntity_ExtendedPowerMultiBlockBase + implements ISurvivalConstructable { + + private static final String STRUCTURE_PIECE_MAIN = "main"; + private static final String STRUCTURE_PIECE_MAIN_SURVIVAL = "main_survival"; + + /** + * Maximum distance in each axis between the purification plant main controller and the controller blocks of the + * purification plant units. + */ + public static final int MAX_UNIT_DISTANCE = 32; + + /** + * Time in ticks for a full processing cycle to complete. + */ + public static final int CYCLE_TIME_TICKS = 120 * SECONDS; + + /** + * Stores all purification units linked to this controller. + * Normally all units in this list should be valid and unique, if not then there is a bug where they are not being + * unlinked properly on block destruction/relinking. + */ + private final List mLinkedUnits = new ArrayList<>(); + + private static final IStructureDefinition STRUCTURE_DEFINITION = StructureDefinition + .builder() + .addShape(STRUCTURE_PIECE_MAIN, PurificationPlantStructureString.STRUCTURE_STRING) + // Create an identical structure for survival autobuild, with water replaced with air + .addShape( + STRUCTURE_PIECE_MAIN_SURVIVAL, + Arrays.stream(PurificationPlantStructureString.STRUCTURE_STRING) + .map( + sa -> Arrays.stream(sa) + .map(s -> s.replaceAll("F", " ")) + .toArray(String[]::new)) + .toArray(String[][]::new)) + // Superplasticizer-treated high strength concrete + .addElement('A', ofBlock(GregTech_API.sBlockCasings9, 3)) + // Sterile Water Plant Casing + .addElement('B', ofBlock(GregTech_API.sBlockCasings9, 4)) + // Reinforced Sterile Water Plant Casing + .addElement('C', ofBlock(GregTech_API.sBlockCasings9, 5)) + // Tinted Industrial Glass + .addElement('D', ofBlockAnyMeta(GregTech_API.sBlockTintedGlass, 0)) + .addElement('F', ofBlock(Blocks.water, 0)) + .addElement('G', ofFrame(Materials.Tungsten)) + // Hatch space + .addElement( + 'H', + ofChain( + lazy( + t -> GT_StructureUtility.buildHatchAdder() + .atLeastList(t.getAllowedHatches()) + .dot(1) + .casingIndex(GT_Utility.getCasingTextureIndex(GregTech_API.sBlockCasings9, 4)) + .build()), + ofBlock(GregTech_API.sBlockCasings9, 4))) + .build(); + + public GT_MetaTileEntity_PurificationPlant(int aID, String aName, String aNameRegional) { + super(aID, aName, aNameRegional); + } + + public GT_MetaTileEntity_PurificationPlant(String aName) { + super(aName); + } + + @Override + public void construct(ItemStack stackSize, boolean hintsOnly) { + buildPiece(STRUCTURE_PIECE_MAIN, stackSize, hintsOnly, 3, 6, 0); + } + + @Override + public int survivalConstruct(ItemStack stackSize, int elementBudget, ISurvivalBuildEnvironment env) { + int built = survivialBuildPiece(STRUCTURE_PIECE_MAIN_SURVIVAL, stackSize, 3, 6, 0, elementBudget, env, true); + if (built == -1) { + GT_Utility.sendChatToPlayer( + env.getActor(), + EnumChatFormatting.GREEN + "Auto placing done ! Now go place the water yourself !"); + return 0; + } + return built; + } + + @Override + public IStructureDefinition getStructureDefinition() { + return STRUCTURE_DEFINITION; + } + + @Override + protected GT_Multiblock_Tooltip_Builder createTooltip() { + final GT_Multiblock_Tooltip_Builder tt = new GT_Multiblock_Tooltip_Builder(); + tt.addMachineType("Purification Plant") + .addInfo("Main controller block for the Water Purification Plant.") + .addInfo( + "Freely place " + EnumChatFormatting.YELLOW + + "Purification Units " + + EnumChatFormatting.GRAY + + "within " + + EnumChatFormatting.RED + + MAX_UNIT_DISTANCE + + EnumChatFormatting.GRAY + + " blocks along each axis.") + .addInfo("Left click this controller with a data stick, then right click a purification unit to link.") + .addInfo("Supplies power to linked purification units. This multiblock accepts TecTech energy hatches.") + .addSeparator() + .addInfo( + "Works in fixed time processing cycles of " + EnumChatFormatting.RED + + CYCLE_TIME_TICKS / SECONDS + + EnumChatFormatting.GRAY + + " seconds.") + .addInfo("All linked units follow this cycle.") + .addSeparator() + .addInfo("Every recipe has a base chance of success. Success rate can be boosted") + .addInfo("by using a portion of the target output as a secondary input.") + .addInfo( + EnumChatFormatting.RED + GT_Utility.formatNumbers(WATER_BOOST_NEEDED_FLUID * 100) + + "%" + + EnumChatFormatting.GRAY + + " of output yield will be consumed in exchange for an") + .addInfo( + "additive " + EnumChatFormatting.RED + + GT_Utility.formatNumbers(WATER_BOOST_BONUS_CHANCE * 100) + + "%" + + EnumChatFormatting.GRAY + + " increase to success.") + .addInfo( + "On recipe failure, each purification unit has a " + EnumChatFormatting.RED + + "50%" + + EnumChatFormatting.GRAY + + " chance") + .addInfo("to return water of the same quality as the input or lower.") + .addSeparator() + .addInfo("Every purification unit has a configuration window to configure maximum parallel amount.") + .addInfo( + "This will only scale purified water I/O and power usage. Other catalysts and outputs are unchanged.") + .addSeparator() + .addInfo( + EnumChatFormatting.AQUA + "" + + EnumChatFormatting.ITALIC + + "Contaminants and ionized particles in water can cause significant imperfections in delicate") + .addInfo( + EnumChatFormatting.AQUA + "" + + EnumChatFormatting.ITALIC + + "processes related to the cutting and engraving of silicon wafers and chips. It is crucial that") + .addInfo( + EnumChatFormatting.AQUA + "" + + EnumChatFormatting.ITALIC + + "the water is systematically purified through a series of increasingly precise and complex") + .addInfo( + EnumChatFormatting.AQUA + "" + + EnumChatFormatting.ITALIC + + "purification processes, and this multiblock is the heart of the operation.") + .addInfo(AuthorNotAPenguin) + .beginStructureBlock(7, 9, 8, false) + .addCasingInfoExactlyColored( + "Superplasticizer-Treated High Strength Concrete", + EnumChatFormatting.GRAY, + 56, + EnumChatFormatting.GOLD, + false) + .addCasingInfoExactlyColored( + "Sterile Water Plant Casing", + EnumChatFormatting.GRAY, + 77, + EnumChatFormatting.GOLD, + false) + .addCasingInfoRangeColored( + "Reinforced Sterile Water Plant Casing", + EnumChatFormatting.GRAY, + 71, + 72, + EnumChatFormatting.GOLD, + false) + .addCasingInfoExactlyColored( + "Tungsten Frame Box", + EnumChatFormatting.GRAY, + 30, + EnumChatFormatting.GOLD, + false) + .addCasingInfoExactlyColored( + "Tinted Industrial Glass", + EnumChatFormatting.GRAY, + 6, + EnumChatFormatting.GOLD, + false) + .addCasingInfoExactlyColored("Reinforced Door", EnumChatFormatting.GRAY, 1, EnumChatFormatting.GOLD, false) + .addController("Front center") + .addEnergyHatch(EnumChatFormatting.GOLD + "1", 1) + .addMaintenanceHatch(EnumChatFormatting.GOLD + "1", 1) + .addStructureInfo("Requires water to be placed in the tank.") + .addStructureInfo("Use the StructureLib Hologram Projector to build the structure.") + .toolTipFinisher("GregTech"); + return tt; + } + + @Override + public IMetaTileEntity newMetaEntity(IGregTechTileEntity aTileEntity) { + return new GT_MetaTileEntity_PurificationPlant(this.mName); + } + + @Override + public ITexture[] getTexture(IGregTechTileEntity baseMetaTileEntity, ForgeDirection side, ForgeDirection facing, + int colorIndex, boolean active, boolean redstoneLevel) { + if (side == facing) { + if (active) return new ITexture[] { + Textures.BlockIcons + .getCasingTextureForId(GT_Utility.getCasingTextureIndex(GregTech_API.sBlockCasings9, 4)), + TextureFactory.builder() + .addIcon(OVERLAY_FRONT_PROCESSING_ARRAY_ACTIVE) + .extFacing() + .build(), + TextureFactory.builder() + .addIcon(OVERLAY_FRONT_PROCESSING_ARRAY_ACTIVE_GLOW) + .extFacing() + .glow() + .build() }; + return new ITexture[] { + Textures.BlockIcons + .getCasingTextureForId(GT_Utility.getCasingTextureIndex(GregTech_API.sBlockCasings9, 4)), + TextureFactory.builder() + .addIcon(OVERLAY_FRONT_PROCESSING_ARRAY) + .extFacing() + .build(), + TextureFactory.builder() + .addIcon(OVERLAY_FRONT_PROCESSING_ARRAY_GLOW) + .extFacing() + .glow() + .build() }; + } + return new ITexture[] { Textures.BlockIcons + .getCasingTextureForId(GT_Utility.getCasingTextureIndex(GregTech_API.sBlockCasings9, 4)) }; + } + + @Override + public boolean isCorrectMachinePart(ItemStack aStack) { + return true; + } + + private List> getAllowedHatches() { + return ImmutableList.of(Maintenance, Energy, ExoticEnergy); + } + + @Override + public boolean checkMachine(IGregTechTileEntity aBaseMetaTileEntity, ItemStack aStack) { + // Check self + if (!checkPiece(STRUCTURE_PIECE_MAIN, 3, 6, 0)) { + return false; + } + + // Check hatches + if (!checkHatches()) { + return false; + } + + // using nano forge method of detecting hatches. + if (!checkExoticAndNormalEnergyHatches()) { + return false; + } + + return true; + } + + private boolean checkHatches() { + // Exactly one maintenance hatch is required + return mMaintenanceHatches.size() == 1; + } + + @Override + public void onPostTick(IGregTechTileEntity aBaseMetaTileEntity, long aTick) { + super.onPostTick(aBaseMetaTileEntity, aTick); + + if (aBaseMetaTileEntity.isServerSide()) { + // Trigger structure check of linked units, but never all in the same tick, and at most once per cycle. + for (int i = 0; i < mLinkedUnits.size(); ++i) { + if (aTick % CYCLE_TIME_TICKS == i) { + LinkedPurificationUnit unit = mLinkedUnits.get(i); + boolean structure = unit.metaTileEntity() + .checkStructure(true); + // If unit was active but deformed, set as inactive + if (unit.isActive() && !structure) { + unit.setActive(false); + // Also remember to recalculate power usage, since otherwise the deformed unit will + // keep drawing power + this.lEUt = -calculateEffectivePowerUsage(); + } + } + } + } + } + + @Override + protected void runMachine(IGregTechTileEntity aBaseMetaTileEntity, long aTick) { + updateCycleProgress(); + // Calculate efficiency based on maintenance issues + if (mMaxProgresstime > 0) { + mEfficiency = Math.max( + 0, + Math.min( + mEfficiency + mEfficiencyIncrease, + getMaxEfficiency(mInventory[1]) - ((getIdealStatus() - getRepairStatus()) * 1000))); + } + } + + private void updateCycleProgress() { + // Since the plant does not run recipes directly, we just continuously loop the base cycle + if (mMachine) { + // cycle is running, so simply advance it + if (mMaxProgresstime > 0) { + // onRunningTick is responsible for draining power + if (onRunningTick(mInventory[1])) { + markDirty(); + mProgresstime += 1; + // Update progress time for active units + for (LinkedPurificationUnit unit : this.mLinkedUnits) { + if (unit.isActive()) { + GT_MetaTileEntity_PurificationUnitBase metaTileEntity = unit.metaTileEntity(); + metaTileEntity.mProgresstime = mProgresstime; + } + } + // Cycle finished + if (mProgresstime >= mMaxProgresstime) { + this.endCycle(); + } + } else { + // Power drain failed, shut down all other units due to power loss. + // Note that we do not need to shut down self, as this is done in + // onRunningTick already + for (LinkedPurificationUnit unit : mLinkedUnits) { + if (unit.isActive()) { + unit.metaTileEntity() + .stopMachine(ShutDownReasonRegistry.POWER_LOSS); + } + } + } + } + + // No cycle running, start a new cycle if the machine is turned on + if (mMaxProgresstime == 0 && isAllowedToWork()) { + this.startCycle(); + } + } + } + + private void startCycle() { + mProgresstime = 0; + mMaxProgresstime = CYCLE_TIME_TICKS; + mEfficiency = (10000 - (getIdealStatus() - getRepairStatus()) * 1000); + + // Find active units and notify them that the cycle started + for (LinkedPurificationUnit unit : this.mLinkedUnits) { + GT_MetaTileEntity_PurificationUnitBase metaTileEntity = unit.metaTileEntity(); + PurificationUnitStatus status = metaTileEntity.status(); + // Unit needs to be online to be considered active. + if (status == PurificationUnitStatus.ONLINE) { + // Perform recipe check for unit and start it if successful + if (metaTileEntity.doPurificationRecipeCheck()) { + unit.setActive(true); + metaTileEntity.startCycle(mMaxProgresstime, mProgresstime); + } + } + } + + // After activating all units, calculate power usage + lEUt = -calculateEffectivePowerUsage(); + } + + private void endCycle() { + mMaxProgresstime = 0; + + // Mark all units as inactive and reset their progress time + for (LinkedPurificationUnit unit : this.mLinkedUnits) { + GT_MetaTileEntity_PurificationUnitBase metaTileEntity = unit.metaTileEntity(); + // If this unit was active, end the cycle + if (unit.isActive()) { + metaTileEntity.endCycle(); + } + unit.setActive(false); + } + } + + /** + * Calculate power usage of all units + */ + private long calculateEffectivePowerUsage() { + long euT = 0; + for (LinkedPurificationUnit unit : mLinkedUnits) { + if (unit.isActive()) { + euT += unit.metaTileEntity() + .getActualPowerUsage(); + } + } + return euT; + } + + @Override + public int getMaxEfficiency(ItemStack aStack) { + return 10000; + } + + @Override + public int getDamageToComponent(ItemStack aStack) { + return 0; + } + + @Override + public boolean explodesOnComponentBreak(ItemStack aStack) { + return false; + } + + public void registerLinkedUnit(GT_MetaTileEntity_PurificationUnitBase unit) { + this.mLinkedUnits.add(new LinkedPurificationUnit(unit)); + } + + public void unregisterLinkedUnit(GT_MetaTileEntity_PurificationUnitBase unit) { + this.mLinkedUnits.removeIf(link -> link.metaTileEntity() == unit); + } + + @Override + public void onLeftclick(IGregTechTileEntity aBaseMetaTileEntity, EntityPlayer aPlayer) { + if (!(aPlayer instanceof EntityPlayerMP)) return; + + // Save link data to data stick, very similar to Crafting Input Buffer. + + ItemStack dataStick = aPlayer.inventory.getCurrentItem(); + if (!ItemList.Tool_DataStick.isStackEqual(dataStick, false, true)) return; + + NBTTagCompound tag = new NBTTagCompound(); + tag.setString("type", "PurificationPlant"); + tag.setInteger("x", aBaseMetaTileEntity.getXCoord()); + tag.setInteger("y", aBaseMetaTileEntity.getYCoord()); + tag.setInteger("z", aBaseMetaTileEntity.getZCoord()); + + dataStick.stackTagCompound = tag; + dataStick.setStackDisplayName( + "Purification Plant Link Data Stick (" + aBaseMetaTileEntity + .getXCoord() + ", " + aBaseMetaTileEntity.getYCoord() + ", " + aBaseMetaTileEntity.getZCoord() + ")"); + aPlayer.addChatMessage(new ChatComponentText("Saved Link Data to Data Stick")); + } + + @Override + public String[] getInfoData() { + var ret = new ArrayList(); + // Show linked purification units and their status + ret.add("Linked Purification Units: "); + for (LinkedPurificationUnit unit : this.mLinkedUnits) { + String text = EnumChatFormatting.AQUA + unit.metaTileEntity() + .getLocalName() + ": "; + PurificationUnitStatus status = unit.metaTileEntity() + .status(); + switch (status) { + case ONLINE -> { + text = text + EnumChatFormatting.GREEN + "Online"; + } + case DISABLED -> { + text = text + EnumChatFormatting.YELLOW + "Disabled"; + } + case INCOMPLETE_STRUCTURE -> { + text = text + EnumChatFormatting.RED + "Incomplete Structure"; + } + } + ret.add(text); + } + return ret.toArray(new String[0]); + } + + @Override + public void onBlockDestroyed() { + // When the controller is destroyed we want to notify all currently linked units + for (LinkedPurificationUnit unit : this.mLinkedUnits) { + unit.metaTileEntity() + .unlinkController(); + } + super.onBlockDestroyed(); + } + + private void drawTopText(DynamicPositionedColumn screenElements) { + screenElements.setSynced(false) + .setSpace(0) + .setPos(10, 8); + + screenElements + .widget( + new TextWidget(GT_Utility.trans("138", "Incomplete Structure.")).setDefaultColor(EnumChatFormatting.RED) + .setEnabled(widget -> !mMachine)) + .widget(new FakeSyncWidget.BooleanSyncer(() -> mMachine, val -> mMachine = val)); + + screenElements.widget( + new TextWidget("Hit with Soft Mallet to start.").setDefaultColor(EnumChatFormatting.BLACK) + .setEnabled( + widget -> getBaseMetaTileEntity().getErrorDisplayID() == 0 && !getBaseMetaTileEntity().isActive())) + .widget( + new FakeSyncWidget.IntegerSyncer( + () -> getBaseMetaTileEntity().getErrorDisplayID(), + val -> getBaseMetaTileEntity().setErrorDisplayID(val))) + .widget( + new FakeSyncWidget.BooleanSyncer( + () -> getBaseMetaTileEntity().isActive(), + val -> getBaseMetaTileEntity().setActive(val))); + screenElements.widget( + new TextWidget(GT_Utility.trans("142", "Running perfectly.")).setDefaultColor(EnumChatFormatting.GREEN) + .setEnabled( + widget -> getBaseMetaTileEntity().getErrorDisplayID() == 0 && getBaseMetaTileEntity().isActive())); + screenElements.widget( + TextWidget.dynamicString( + () -> getBaseMetaTileEntity().getLastShutDownReason() + .getDisplayString()) + .setSynced(false) + .setTextAlignment(Alignment.CenterLeft) + .setEnabled( + widget -> shouldDisplayShutDownReason() && !getBaseMetaTileEntity().isActive() + && GT_Utility.isStringValid( + getBaseMetaTileEntity().getLastShutDownReason() + .getDisplayString()) + && getBaseMetaTileEntity().wasShutdown())) + .widget( + new ShutDownReasonSyncer( + () -> getBaseMetaTileEntity().getLastShutDownReason(), + reason -> getBaseMetaTileEntity().setShutDownReason(reason))) + .widget( + new FakeSyncWidget.BooleanSyncer( + () -> getBaseMetaTileEntity().wasShutdown(), + wasShutDown -> getBaseMetaTileEntity().setShutdownStatus(wasShutDown))); + screenElements.widget( + TextWidget.dynamicString(this::generateCurrentRecipeInfoString) + .setSynced(false) + .setTextAlignment(Alignment.CenterLeft) + .setEnabled(widget -> (mMaxProgresstime > 0))) + .widget(new FakeSyncWidget.IntegerSyncer(() -> mProgresstime, val -> mProgresstime = val)) + .widget(new FakeSyncWidget.IntegerSyncer(() -> mMaxProgresstime, val -> mMaxProgresstime = val)); + } + + private final int STATUS_WINDOW_ID = 10; + + private ModularWindow createStatusWindow(final EntityPlayer player) { + final int windowWidth = 260; + final int windowHeight = 200; + ModularWindow.Builder builder = ModularWindow.builder(windowWidth, windowHeight); + builder.setBackground(GT_UITextures.BACKGROUND_SINGLEBLOCK_DEFAULT); + builder.widget( + ButtonWidget.closeWindowButton(true) + .setPos(windowWidth - 15, 3)); + + // Title widget + builder.widget( + new TextWidget(EnumChatFormatting.BOLD + "Purification Unit Status").setTextAlignment(Alignment.Center) + .setPos(5, 10) + .setSize(windowWidth, 8)); + + int currentYPosition = 20; + Scrollable mainDisp = new Scrollable().setVerticalScroll(); + + int rowHeight = 20; + for (int i = 0; i < this.mLinkedUnits.size(); i++) { + mainDisp.widget(makeUnitStatusWidget(mLinkedUnits.get(i)).setPos(0, rowHeight * (i + 1))); + } + + builder.widget( + mainDisp.setPos(5, currentYPosition) + .setSize(windowWidth - 10, windowHeight - currentYPosition - 5)); + return builder.build(); + } + + private Widget makeStatusWindowButton() { + TextButtonWidget widget = (TextButtonWidget) new TextButtonWidget("Status").setLeftMargin(4) + .setSize(40, 16) + .setPos(10, 40); + widget.button() + .setOnClick( + (clickData, w) -> { + if (!w.isClient()) w.getContext() + .openSyncedWindow(STATUS_WINDOW_ID); + }) + .setBackground(GT_UITextures.BUTTON_STANDARD); + widget.text() + .setTextAlignment(Alignment.CenterLeft) + .setDefaultColor(EnumChatFormatting.BLACK); + return widget; + } + + private Widget makeUnitStatusWidget(LinkedPurificationUnit unit) { + // Draw small machine controller icon + DynamicPositionedRow row = new DynamicPositionedRow(); + ItemStackHandler machineIcon = new ItemStackHandler(1); + machineIcon.setStackInSlot( + 0, + unit.metaTileEntity() + .getStackForm(1)); + + row.widget( + SlotWidget.phantom(machineIcon, 0) + .disableInteraction() + .setPos(0, 0)) + .setSize(20, 20); + + // Display machine name and status + String name = unit.metaTileEntity() + .getLocalName(); + + row.widget( + TextWidget.dynamicString(() -> name + " " + unit.getStatusString()) + .setSynced(false) + .setTextAlignment(Alignment.CenterLeft) + .setPos(25, 0) + .setSize(0, 20)) + .widget(new FakeSyncWidget.StringSyncer(() -> name, _name -> {})) + .widget( + unit.metaTileEntity() + .makeSyncerWidgets()) + .widget(new FakeSyncWidget.BooleanSyncer(unit::isActive, unit::setActive));; + + return row; + } + + @Override + public void addUIWidgets(ModularWindow.Builder builder, UIBuildContext buildContext) { + + buildContext.addSyncedWindow(STATUS_WINDOW_ID, this::createStatusWindow); + + // Draw basic recipe info + final DynamicPositionedColumn controlTextArea = new DynamicPositionedColumn(); + drawTopText(controlTextArea); + builder.widget(controlTextArea); + + // Draw line separator + builder.widget( + new Rectangle().setColor(Color.rgb(114, 120, 139)) + .asWidget() + .setSizeProvider((screenSize, window, parent) -> new Size(window.getSize().width - 8, 2)) + .setPos(3, 32)); + + // Add status window button + builder.widget(makeStatusWindowButton()); + + // Add parallel count number input + + builder.widget(createPowerSwitchButton(builder)); + + // Add value syncers, note that we do this here so + // everything is updated once the status gui opens + addSyncers(builder); + } + + private void addSyncers(ModularWindow.Builder builder) { + // Sync connection list to client + builder.widget(new FakeSyncWidget.ListSyncer<>(() -> mLinkedUnits, links -> { + mLinkedUnits.clear(); + mLinkedUnits.addAll(links); + }, (buffer, link) -> { + // Try to save link data to NBT, so we can reconstruct it on client + try { + buffer.writeNBTTagCompoundToBuffer(link.writeLinkDataToNBT()); + } catch (IOException e) { + GT_Log.err.println(e.getCause()); + } + }, buffer -> { + // Try to load link data from NBT compound as constructed above. + try { + return new LinkedPurificationUnit(buffer.readNBTTagCompoundFromBuffer()); + } catch (IOException e) { + GT_Log.err.println(e.getCause()); + } + return null; + })); + } +} diff --git a/src/main/java/gregtech/common/tileentities/machines/multi/purification/GT_MetaTileEntity_PurificationUnitBase.java b/src/main/java/gregtech/common/tileentities/machines/multi/purification/GT_MetaTileEntity_PurificationUnitBase.java new file mode 100644 index 0000000000..0abf9d525f --- /dev/null +++ b/src/main/java/gregtech/common/tileentities/machines/multi/purification/GT_MetaTileEntity_PurificationUnitBase.java @@ -0,0 +1,793 @@ +package gregtech.common.tileentities.machines.multi.purification; + +import static gregtech.api.metatileentity.BaseTileEntity.TOOLTIP_DELAY; +import static net.minecraft.util.StatCollector.translateToLocal; + +import java.util.ArrayList; +import java.util.Comparator; +import java.util.EnumSet; +import java.util.List; +import java.util.Set; +import java.util.concurrent.ThreadLocalRandom; +import java.util.stream.Stream; + +import net.minecraft.block.Block; +import net.minecraft.entity.player.EntityPlayer; +import net.minecraft.entity.player.EntityPlayerMP; +import net.minecraft.item.ItemStack; +import net.minecraft.nbt.NBTTagCompound; +import net.minecraft.tileentity.TileEntity; +import net.minecraft.util.ChatComponentText; +import net.minecraft.util.EnumChatFormatting; +import net.minecraft.world.World; +import net.minecraftforge.fluids.FluidStack; + +import org.jetbrains.annotations.NotNull; + +import com.gtnewhorizons.modularui.api.drawable.IDrawable; +import com.gtnewhorizons.modularui.api.drawable.UITexture; +import com.gtnewhorizons.modularui.api.math.Alignment; +import com.gtnewhorizons.modularui.api.math.Color; +import com.gtnewhorizons.modularui.api.math.Size; +import com.gtnewhorizons.modularui.api.screen.ModularWindow; +import com.gtnewhorizons.modularui.api.screen.UIBuildContext; +import com.gtnewhorizons.modularui.api.widget.Widget; +import com.gtnewhorizons.modularui.common.widget.ButtonWidget; +import com.gtnewhorizons.modularui.common.widget.FakeSyncWidget; +import com.gtnewhorizons.modularui.common.widget.MultiChildWidget; +import com.gtnewhorizons.modularui.common.widget.TextWidget; +import com.gtnewhorizons.modularui.common.widget.textfield.NumericWidget; + +import gregtech.api.enums.ItemList; +import gregtech.api.enums.Materials; +import gregtech.api.enums.VoidingMode; +import gregtech.api.gui.modularui.GT_UITextures; +import gregtech.api.interfaces.tileentity.IGregTechTileEntity; +import gregtech.api.metatileentity.implementations.GT_MetaTileEntity_ExtendedPowerMultiBlockBase; +import gregtech.api.recipe.RecipeMap; +import gregtech.api.recipe.check.CheckRecipeResult; +import gregtech.api.recipe.check.CheckRecipeResultRegistry; +import gregtech.api.recipe.metadata.PurificationPlantBaseChanceKey; +import gregtech.api.util.GT_ModHandler; +import gregtech.api.util.GT_Recipe; +import gregtech.api.util.GT_Utility; +import gregtech.common.blocks.GT_Block_Casings_Abstract; +import mcp.mobius.waila.api.IWailaConfigHandler; +import mcp.mobius.waila.api.IWailaDataAccessor; + +/** + * Base class for purification units. This class handles all shared behaviour between units. + * When inheriting from this, make sure to call super.loadNBTData() and super.saveNBTData() + * if you override these methods, or linking will break. + */ +public abstract class GT_MetaTileEntity_PurificationUnitBase> + extends GT_MetaTileEntity_ExtendedPowerMultiBlockBase { + + /** + * Ratio of output fluid that needs to be inserted back as input to trigger a "water boost". + * Must be in [0, 1]. + */ + public static final float WATER_BOOST_NEEDED_FLUID = 0.1f; + /** + * Additive bonus to success chance when water boost is active. + * Must be in [0, 1] + */ + public static final float WATER_BOOST_BONUS_CHANCE = 0.15f; + + /** + * Small internal enum to report back the various error cases when linking purification units to the + * purification plant. + */ + private enum LinkResult { + /** + * Link target was out of range of the main controller + */ + TOO_FAR, + /** + * No valid GT_MetaTileEntity_PurificationPlant was found at the link target position. + */ + NO_VALID_PLANT, + /** + * Link successful + */ + SUCCESS, + } + + /** + * Coordinates of the main purification plant controller. These can be used to find the controller again + * on world load. + */ + private int controllerX, controllerY, controllerZ; + + /** + * Whether a controller was previously set. + */ + private boolean controllerSet = false; + + /** + * Pointer to the main purification plant controller. + */ + private GT_MetaTileEntity_PurificationPlant controller = null; + + /** + * The current recipe being run in the purification unit. Note that purification unit recipes are a bit special, + * so input and output in the recipe might not exactly match the required inputs and produced outputs. + * For more information, always look at the purification unit tooltip and implementation. + */ + protected GT_Recipe currentRecipe = null; + + /** + * Current chance of the recipe succeeding, always in [0, 100]. A chance above 100 will be interpreted as 100. + */ + protected float currentRecipeChance = 0.0f; + + /** + * Configured parallel amount. Only water I/O and power scale. + */ + protected int maxParallel = 1; + + protected int effectiveParallel = 1; + + protected ArrayList storedFluids = null; + + protected GT_MetaTileEntity_PurificationUnitBase(int aID, String aName, String aNameRegional) { + super(aID, aName, aNameRegional); + } + + protected GT_MetaTileEntity_PurificationUnitBase(String aName) { + super(aName); + } + + @Override + public int getMaxEfficiency(ItemStack aStack) { + return 10000; + } + + @Override + public int getDamageToComponent(ItemStack aStack) { + return 0; + } + + @Override + public boolean explodesOnComponentBreak(ItemStack aStack) { + return false; + } + + @Override + public boolean doRandomMaintenanceDamage() { + // The individual purification unit structures cannot have maintenance issues, so do nothing. + return true; + } + + /** + * Used to more easily grab a correct texture index from a block + meta. + * + * @param block Block to use as base. Must implement GT_Block_Casings_Abstract + * @param meta Metadata of the block to pick the actual block + * @return The correct index into the global texture atlas. + */ + protected static int getTextureIndex(Block block, int meta) { + return ((GT_