From 337594e83a74c432c140b3df3287575b81bce467 Mon Sep 17 00:00:00 2001 From: Raven Szewczyk Date: Thu, 30 May 2024 18:26:10 +0100 Subject: Complete backend rework of the EIG (#2616) * Complete backend rework of the EIG * Mergening Related Updates Also some loader references refactoring * fix (cherry picked from commit 7fd5d7417bddfb6e49ede3986d9a547f15b21289) * More Mergening fixes Updates the declaration of the stem mixin to match the new format. * Inline EIG IC2 bucket constants addresses: https://github.com/GTNewHorizons/GT5-Unofficial/pull/2616#discussion_r1620596497 * Fix Seed Removal in regular seed simulations Should address https://github.com/GTNewHorizons/GT5-Unofficial/pull/2616#discussion_r1620583338 --------- Co-authored-by: Guillaume Mercier <10gui-gui10@live.ca> Co-authored-by: Martin Robertz --- ...MetaTileEntity_ExtremeIndustrialGreenhouse.java | 1570 ++++++++------------ .../multiblock/eigbuckets/EIGFlowerBucket.java | 73 + .../multiblock/eigbuckets/EIGIC2Bucket.java | 905 +++++++++++ .../eigbuckets/EIGRainbowCactusBucket.java | 77 + .../multiblock/eigbuckets/EIGSeedBucket.java | 286 ++++ .../multiblock/eigbuckets/EIGStemBucket.java | 158 ++ .../gregtech/multiblock/eigmodes/EIGIC2Mode.java | 95 ++ .../multiblock/eigmodes/EIGNormalMode.java | 91 ++ 8 files changed, 2339 insertions(+), 916 deletions(-) create mode 100644 src/main/java/kubatech/tileentity/gregtech/multiblock/eigbuckets/EIGFlowerBucket.java create mode 100644 src/main/java/kubatech/tileentity/gregtech/multiblock/eigbuckets/EIGIC2Bucket.java create mode 100644 src/main/java/kubatech/tileentity/gregtech/multiblock/eigbuckets/EIGRainbowCactusBucket.java create mode 100644 src/main/java/kubatech/tileentity/gregtech/multiblock/eigbuckets/EIGSeedBucket.java create mode 100644 src/main/java/kubatech/tileentity/gregtech/multiblock/eigbuckets/EIGStemBucket.java create mode 100644 src/main/java/kubatech/tileentity/gregtech/multiblock/eigmodes/EIGIC2Mode.java create mode 100644 src/main/java/kubatech/tileentity/gregtech/multiblock/eigmodes/EIGNormalMode.java (limited to 'src/main/java/kubatech/tileentity/gregtech/multiblock') diff --git a/src/main/java/kubatech/tileentity/gregtech/multiblock/GT_MetaTileEntity_ExtremeIndustrialGreenhouse.java b/src/main/java/kubatech/tileentity/gregtech/multiblock/GT_MetaTileEntity_ExtremeIndustrialGreenhouse.java index cdab236542..a45e66142b 100644 --- a/src/main/java/kubatech/tileentity/gregtech/multiblock/GT_MetaTileEntity_ExtremeIndustrialGreenhouse.java +++ b/src/main/java/kubatech/tileentity/gregtech/multiblock/GT_MetaTileEntity_ExtremeIndustrialGreenhouse.java @@ -24,6 +24,7 @@ import static com.gtnewhorizon.structurelib.structure.StructureUtility.ofBlock; import static com.gtnewhorizon.structurelib.structure.StructureUtility.ofChain; import static com.gtnewhorizon.structurelib.structure.StructureUtility.onElementPass; import static com.gtnewhorizon.structurelib.structure.StructureUtility.transpose; +import static gregtech.api.enums.Mods.BartWorks; import static gregtech.api.enums.Mods.ProjectRedIllumination; import static gregtech.api.enums.Mods.RandomThings; import static gregtech.api.enums.Textures.BlockIcons.OVERLAY_FRONT_DISTILLATION_TOWER; @@ -31,45 +32,36 @@ import static gregtech.api.enums.Textures.BlockIcons.OVERLAY_FRONT_DISTILLATION_ import static gregtech.api.enums.Textures.BlockIcons.OVERLAY_FRONT_DISTILLATION_TOWER_ACTIVE_GLOW; import static gregtech.api.enums.Textures.BlockIcons.OVERLAY_FRONT_DISTILLATION_TOWER_GLOW; import static gregtech.api.util.GT_StructureUtility.ofHatchAdder; +import static gregtech.api.util.GT_Utility.filterValidMTEs; import static kubatech.api.Variables.Author; import static kubatech.api.Variables.StructureHologram; import static kubatech.api.utils.ItemUtils.readItemStackFromNBT; -import static kubatech.api.utils.ItemUtils.writeItemStackToNBT; import java.io.IOException; import java.lang.ref.WeakReference; import java.util.ArrayList; import java.util.Arrays; -import java.util.Collections; +import java.util.Collection; +import java.util.Comparator; import java.util.HashMap; import java.util.Iterator; +import java.util.LinkedList; import java.util.List; import java.util.Map; -import java.util.Objects; -import java.util.Random; +import java.util.stream.Collectors; import net.minecraft.block.Block; -import net.minecraft.block.BlockFlower; -import net.minecraft.block.BlockStem; import net.minecraft.client.Minecraft; +import net.minecraft.entity.item.EntityItem; import net.minecraft.entity.player.EntityPlayer; import net.minecraft.entity.player.EntityPlayerMP; import net.minecraft.init.Blocks; -import net.minecraft.init.Items; -import net.minecraft.inventory.InventoryCrafting; import net.minecraft.inventory.Slot; -import net.minecraft.item.Item; -import net.minecraft.item.ItemBlock; -import net.minecraft.item.ItemSeedFood; -import net.minecraft.item.ItemSeeds; import net.minecraft.item.ItemStack; -import net.minecraft.item.crafting.CraftingManager; -import net.minecraft.item.crafting.IRecipe; import net.minecraft.nbt.NBTTagCompound; -import net.minecraft.tileentity.TileEntity; +import net.minecraft.nbt.NBTTagList; import net.minecraft.util.EnumChatFormatting; import net.minecraft.world.World; -import net.minecraftforge.common.IPlantable; import net.minecraftforge.common.util.ForgeDirection; import net.minecraftforge.fluids.Fluid; import net.minecraftforge.fluids.FluidRegistry; @@ -83,7 +75,6 @@ import com.gtnewhorizon.structurelib.structure.IStructureDefinition; import com.gtnewhorizon.structurelib.structure.StructureDefinition; import com.gtnewhorizons.modularui.api.ModularUITextures; import com.gtnewhorizons.modularui.api.drawable.Text; -import com.gtnewhorizons.modularui.api.drawable.shapes.Rectangle; import com.gtnewhorizons.modularui.api.math.Color; import com.gtnewhorizons.modularui.api.math.MainAxisAlignment; import com.gtnewhorizons.modularui.api.screen.ModularUIContext; @@ -101,12 +92,11 @@ import com.gtnewhorizons.modularui.common.widget.FakeSyncWidget; import com.gtnewhorizons.modularui.common.widget.SlotWidget; import com.gtnewhorizons.modularui.common.widget.TextWidget; -import cpw.mods.fml.common.registry.GameRegistry; import cpw.mods.fml.relauncher.Side; import cpw.mods.fml.relauncher.SideOnly; import gregtech.api.GregTech_API; +import gregtech.api.enums.GTVoltageIndex; import gregtech.api.enums.GT_Values; -import gregtech.api.enums.ItemList; import gregtech.api.enums.Materials; import gregtech.api.enums.Textures; import gregtech.api.gui.modularui.GT_UITextures; @@ -114,28 +104,26 @@ 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_Energy; -import gregtech.api.metatileentity.implementations.GT_MetaTileEntity_Hatch_Input; -import gregtech.api.metatileentity.implementations.GT_MetaTileEntity_Hatch_MultiInput; +import gregtech.api.metatileentity.implementations.GT_MetaTileEntity_Hatch_OutputBus; import gregtech.api.recipe.check.CheckRecipeResult; import gregtech.api.recipe.check.CheckRecipeResultRegistry; import gregtech.api.recipe.check.SimpleCheckRecipeResult; import gregtech.api.render.TextureFactory; import gregtech.api.util.GT_Multiblock_Tooltip_Builder; import gregtech.api.util.GT_Utility; -import gregtech.common.GT_DummyWorld; -import gregtech.common.blocks.GT_Block_Ores_Abstract; -import gregtech.common.blocks.GT_Item_Ores; -import gregtech.common.blocks.GT_TileEntity_Ores; -import ic2.api.crops.CropCard; -import ic2.api.crops.Crops; -import ic2.core.Ic2Items; -import ic2.core.crop.TileEntityCrop; +import gregtech.api.util.VoidProtectionHelper; +import gregtech.common.tileentities.machines.GT_MetaTileEntity_Hatch_OutputBus_ME; import ic2.core.init.BlocksItems; import ic2.core.init.InternalName; import kubatech.Tags; -import kubatech.api.DynamicInventory; +import kubatech.api.EIGDynamicInventory; +import kubatech.api.eig.EIGBucket; +import kubatech.api.eig.EIGDropTable; +import kubatech.api.eig.EIGMode; +import kubatech.api.enums.EIGModes; import kubatech.api.implementations.KubaTechGTMultiBlockBase; import kubatech.client.effect.CropRenderer; +import kubatech.tileentity.gregtech.multiblock.eigbuckets.EIGIC2Bucket; @SuppressWarnings("unused") public class GT_MetaTileEntity_ExtremeIndustrialGreenhouse @@ -159,36 +147,69 @@ public class GT_MetaTileEntity_ExtremeIndustrialGreenhouse * Changing T one number down will buff the EIG twice, as well as changing it up will nerf the EIG twice * (That is because accelerators are imperfectly scaled in game LV = 2x, MV = 4x, ...) */ - public static final int EIG_BALANCE_IC2_ACCELERATOR_TIER = 5; // IV - - /*** - * All crops above this threshold will die if WEED-EX 9000 isn't supplied. - * Consider changing this value after making changes to the EIG_BALANCE_IC2_ACCELERATOR_TIER. - */ - private static final int EIG_CROP_LIMIT_FOR_WEEDEX9000_REQUIREMENT = 1000; + public static final int EIG_BALANCE_IC2_ACCELERATOR_TIER = GTVoltageIndex.IV; + public static final int EIG_BALANCE_REGULAR_MODE_MIN_TIER = GTVoltageIndex.EV; + public static final int EIG_BALANCE_IC2_MODE_MIN_TIER = EIG_BALANCE_IC2_ACCELERATOR_TIER + 1; + public static final double EIG_BALANCE_MAX_FERTILIZER_BOOST = 4.0d; + public static final int EIG_BALANCE_WEED_EX_USAGE_BEGINS_AT = 1000; + public static final int EIG_BALANCE_WATER_USAGE_PER_SEED = 1000; + + private static final Fluid WEEDEX_FLUID = Materials.WeedEX9000.mFluid; + private static final LinkedList FERTILIZER_ITEM_LIST = new LinkedList<>(); + + public static void addFertilizerItem(ItemStack fertilizer) { + FERTILIZER_ITEM_LIST.addLast(fertilizer); + } private static final boolean debug = false; /*** * Changing this variable will cause ALL EIGs in the world to regenerate their drop tables. */ - private static final int EIG_MATH_VERSION = 0; + private static final int NBT_REVISION = 1; private static final int CONFIGURATION_WINDOW_ID = 999; - public final List mStorage = new ArrayList<>(); - private int oldVersion = 0; + public final List buckets = new LinkedList<>(); + public final EIGDropTable dropTracker = new EIGDropTable(); + public Collection toMigrate; + public EIGDropTable guiDropTracker = new EIGDropTable(); + private HashMap synchedGUIDropTracker = new HashMap<>(); + private int maxSeedTypes = 0; + private int maxSeedCount = 0; + /** + * The setup phase of the EIG. 0 operation. 1 input. 2 output. + */ + private int setupPhase = 1; + /** + * The amount of water used per cycle. + */ + private int waterUsage = 0; + /** + * The tier of the glass on the EIG. + */ + private byte glassTier = 0; + /** + * The Amount of Weed-EX used per cycle. + */ + private int weedEXUsage = 0; + /** + * The mode that the EIG is in. + */ + private EIGMode mode = EIGModes.Normal; + /** + * Determines whether new IC2 buckets will use no humidity for their growth speed calculation. + */ + private boolean useNoHumidity = false; + + public boolean isInNoHumidityMode() { + return this.useNoHumidity; + } + + // region structure stuff + private int mCasing = 0; - public int mMaxSlots = 0; - private int setupphase = 1; - private boolean isIC2Mode = false; - private byte glasTier = 0; - private int waterusage = 0; - private int weedexusage = 0; - private boolean isNoHumidity = false; private static final int CASING_INDEX = 49; private static final String STRUCTURE_PIECE_MAIN = "main"; - private static final Item forestryfertilizer = GameRegistry.findItem("Forestry", "fertilizerCompound"); - private static final Fluid weedex = Materials.WeedEX9000.mFluid; private static final IStructureDefinition STRUCTURE_DEFINITION = StructureDefinition .builder() .addShape( @@ -220,12 +241,16 @@ public class GT_MetaTileEntity_ExtremeIndustrialGreenhouse .addElement( 'l', ProjectRedIllumination.isModLoaded() - ? ofBlock(Block.getBlockFromName("ProjRed|Illumination:projectred.illumination.lamp"), 10) + ? ofChain( + ofBlock(Block.getBlockFromName("ProjRed|Illumination:projectred.illumination.lamp"), 10), + ofBlock(Block.getBlockFromName("ProjRed|Illumination:projectred.illumination.lamp"), 26)) : ofChain(ofBlock(Blocks.redstone_lamp, 0), ofBlock(Blocks.lit_redstone_lamp, 0))) .addElement( 'g', - BorosilicateGlass - .ofBoroGlass((byte) 0, (byte) 1, Byte.MAX_VALUE, (te, t) -> te.glasTier = t, te -> te.glasTier)) + BartWorks.isModLoaded() + ? BorosilicateGlass + .ofBoroGlass((byte) 0, (byte) 1, Byte.MAX_VALUE, (te, t) -> te.glassTier = t, te -> te.glassTier) + : onElementPass(t -> t.glassTier = 100, ofBlock(Blocks.glass, 0))) .addElement( 'd', ofBlock( @@ -237,68 +262,33 @@ public class GT_MetaTileEntity_ExtremeIndustrialGreenhouse ofChain(ofBlock(Blocks.water, 0), ofBlock(BlocksItems.getFluidBlock(InternalName.fluidDistilledWater), 0))) .build(); - public GT_MetaTileEntity_ExtremeIndustrialGreenhouse(int aID, String aName, String aNameRegional) { - super(aID, aName, aNameRegional); - } - - public GT_MetaTileEntity_ExtremeIndustrialGreenhouse(String aName) { - super(aName); - } - @Override - public void onRemoval() { - super.onRemoval(); - if (getBaseMetaTileEntity().isServerSide()) tryOutputAll(mStorage, s -> { - ArrayList l = new ArrayList<>(2); - l.add(((GreenHouseSlot) s).input.copy()); - if (((GreenHouseSlot) s).undercrop != null) l.add(((GreenHouseSlot) s).undercrop.copy()); - return l; - }); + public IStructureDefinition getStructureDefinition() { + return STRUCTURE_DEFINITION; } @Override - public void onScrewdriverRightClick(ForgeDirection side, EntityPlayer aPlayer, float aX, float aY, float aZ) { - if (aPlayer.isSneaking()) { - if (this.mMaxProgresstime > 0) { - GT_Utility.sendChatToPlayer(aPlayer, "You can't change IC2 mode if the machine is working!"); - return; - } - if (!mStorage.isEmpty()) { - GT_Utility.sendChatToPlayer(aPlayer, "You can't change IC2 mode if there are seeds inside!"); - return; - } - this.isIC2Mode = !this.isIC2Mode; - GT_Utility.sendChatToPlayer(aPlayer, "IC2 mode is now " + (this.isIC2Mode ? "enabled" : "disabled.")); - } else { - if (this.mMaxProgresstime > 0) { - GT_Utility.sendChatToPlayer(aPlayer, "You can't enable/disable setup if the machine is working!"); - return; - } - this.setupphase++; - if (this.setupphase == 3) this.setupphase = 0; - GT_Utility.sendChatToPlayer( - aPlayer, - "EIG is now running in " + (this.setupphase == 1 ? "setup mode (input)." - : (this.setupphase == 2 ? "setup mode (output)." : "normal operation."))); - } - } + public boolean checkMachine(IGregTechTileEntity iGregTechTileEntity, ItemStack itemStack) { + mCasing = 0; + glassTier = 0; + if (debug) glassTier = 8; - @Override - public boolean onWireCutterRightClick(ForgeDirection side, ForgeDirection wrenchingSide, EntityPlayer aPlayer, - float aX, float aY, float aZ) { - isNoHumidity = !isNoHumidity; - GT_Utility.sendChatToPlayer(aPlayer, "Give incoming crops no humidity " + isNoHumidity); - return true; - } + if (!checkPiece(STRUCTURE_PIECE_MAIN, 2, 5, 0)) return false; - @Override - public IMetaTileEntity newMetaEntity(IGregTechTileEntity iGregTechTileEntity) { - return new GT_MetaTileEntity_ExtremeIndustrialGreenhouse(this.mName); + if (this.glassTier < 8 && !this.mEnergyHatches.isEmpty()) + for (GT_MetaTileEntity_Hatch_Energy hatchEnergy : this.mEnergyHatches) + if (this.glassTier < hatchEnergy.mTier) return false; + + boolean valid = this.mMaintenanceHatches.size() == 1 && !this.mEnergyHatches.isEmpty() && this.mCasing >= 70; + + if (valid) this.updateSeedLimits(); + + return valid; } @Override - public IStructureDefinition getStructureDefinition() { - return STRUCTURE_DEFINITION; + public void construct(ItemStack itemStack, boolean b) { + buildPiece(STRUCTURE_PIECE_MAIN, itemStack, b, 2, 5, 0); } @Override @@ -306,51 +296,37 @@ public class GT_MetaTileEntity_ExtremeIndustrialGreenhouse return (d, r, f) -> d.offsetY == 0 && r.isNotRotated() && f.isNotFlipped(); } + // endregion structure stuff + + // region tooltip + @Override protected GT_Multiblock_Tooltip_Builder createTooltip() { GT_Multiblock_Tooltip_Builder tt = new GT_Multiblock_Tooltip_Builder(); + String fertilizerBoostMax = String.format("%.0f", EIG_BALANCE_MAX_FERTILIZER_BOOST * 100); tt.addMachineType("Crop Farm") .addInfo("Controller block for the Extreme Industrial Greenhouse") .addInfo(Author) - .addInfo("Grow your crops like a chad !") + .addInfo("Grow your crops like a chad!") .addInfo("Use screwdriver to enable/change/disable setup mode") .addInfo("Use screwdriver while sneaking to enable/disable IC2 mode") - .addInfo("Use wire cutters to give incoming IC2 crops 0 humidity") - .addInfo("Uses 1000L of water per crop per operation") + .addInfo("Use wire cutters to give incoming IC2 seeds 0 humidity") + .addInfo("Uses " + EIG_BALANCE_WATER_USAGE_PER_SEED + "L of water per seed per operation") .addInfo( - "If there are >= " + EIG_CROP_LIMIT_FOR_WEEDEX9000_REQUIREMENT - + " crops -> Uses 1L of Weed-EX 9000 per crop per second") - .addInfo("Otherwise, around 1% of crops will die each operation") - .addInfo("You can insert fertilizer each operation to get more drops (max +400%)") - .addInfo("-------------------- SETUP MODE --------------------") + "Uses 1L of " + new FluidStack(WEEDEX_FLUID, 1).getLocalizedName() + + " per operation per seed if it contains more than " + + EIG_BALANCE_WEED_EX_USAGE_BEGINS_AT + + " seeds") + .addInfo("Otherwise, around 1% of seeds will be voided each operation") + .addInfo("You can insert fertilizer each operation to get more drops (max + " + fertilizerBoostMax + ")") + .addInfo("--------------------- SETUP MODE ---------------------") .addInfo("Does not take power") .addInfo("There are two modes: input / output") .addInfo("Input mode: machine will take seeds from input bus and plant them") .addInfo("[IC2] You need to also input block that is required under the crop") - .addInfo("Output mode: machine will take planted seeds and output them") - .addInfo("-------------------- NORMAL CROPS --------------------") - .addInfo("Minimal tier: " + voltageTooltipFormatted(4)) - .addInfo("Starting with 1 slot") - .addInfo("Every slot gives 64 crops") - .addInfo("Every tier past " + voltageTooltipFormatted(4) + ", slots are multiplied by 2") - .addInfo("Base process time: 5 sec") - .addInfo( - "Process time is divided by number of tiers past " + voltageTooltipFormatted(3) + " (Minimum 1 sec)") - .addInfo("All crops are grown at the end of the operation") - .addInfo("Will automatically craft seeds if they are not dropped") - .addInfo("1 Fertilizer per 1 crop +200%") - .addInfo("-------------------- IC2 CROPS --------------------") - .addInfo("Minimal tier: " + voltageTooltipFormatted(EIG_BALANCE_IC2_ACCELERATOR_TIER + 1)) - .addInfo("Need " + voltageTooltipFormatted(EIG_BALANCE_IC2_ACCELERATOR_TIER + 1) + " glass tier") - .addInfo("Starting with 4 slots") - .addInfo("Every slot gives 1 crop") - .addInfo( - "Every tier past " + voltageTooltipFormatted(EIG_BALANCE_IC2_ACCELERATOR_TIER + 1) - + ", slots are multiplied by 4") - .addInfo("Process time: 5 sec") - .addInfo("All crops are accelerated by x" + (1 << EIG_BALANCE_IC2_ACCELERATOR_TIER) + " times") - .addInfo("1 Fertilizer per 1 crop +10%") - .addInfo(StructureHologram) + .addInfo("Output mode: machine will take planted seeds and output them"); + EIGModes.addTooltipInfo(tt); + tt.addInfo(StructureHologram) .addSeparator() .beginStructureBlock(5, 6, 5, false) .addController("Front bottom center") @@ -377,33 +353,262 @@ public class GT_MetaTileEntity_ExtremeIndustrialGreenhouse return info.toArray(new String[] {}); } + // endregion tooltip + + // region (de)constructor + + public GT_MetaTileEntity_ExtremeIndustrialGreenhouse(int aID, String aName, String aNameRegional) { + super(aID, aName, aNameRegional); + } + + public GT_MetaTileEntity_ExtremeIndustrialGreenhouse(String aName) { + super(aName); + } + + @Override + public IMetaTileEntity newMetaEntity(IGregTechTileEntity iGregTechTileEntity) { + return new GT_MetaTileEntity_ExtremeIndustrialGreenhouse(this.mName); + } + + @Override + public void onFirstTick(IGregTechTileEntity aBaseMetaTileEntity) { + super.onFirstTick(aBaseMetaTileEntity); + if (this.toMigrate != null) { + // Create the new buckets respectively. + if (this.mode == EIGModes.IC2) { + for (EIGMigrationHolder holder : toMigrate) { + // We will have to revalidate the seeds on the next cycle. + this.buckets + .add(new EIGIC2Bucket(holder.seed, holder.count, holder.supportBlock, holder.useNoHumidity)); + } + } else { + this.mode = EIGModes.Normal; + for (EIGMigrationHolder holder : toMigrate) { + holder.seed.stackSize = holder.count; + EIGBucket bucket = this.mode.tryCreateNewBucket(this, holder.seed, Integer.MAX_VALUE, false); + if (bucket == null) { + // if we somehow can't grow the seed, try ejecting it at least. + holder.seed.stackSize = holder.count; + this.addOutput(holder.seed); + continue; + } + this.buckets.add(bucket); + } + } + } + } + + /** + * Ejects all the seeds when the controller is broken. + */ + @Override + public void onRemoval() { + super.onRemoval(); + + // attempt to empty all buckets + buckets.removeIf(this::tryEmptyBucket); + if (buckets.isEmpty()) return; + + // attempt to drop non outputted items into the world. + IGregTechTileEntity mte = this.getBaseMetaTileEntity(); + for (EIGBucket bucket : this.buckets) { + for (ItemStack stack : bucket.tryRemoveSeed(bucket.getSeedCount(), false)) { + EntityItem entityitem = new EntityItem( + mte.getWorld(), + mte.getXCoord(), + mte.getYCoord(), + mte.getZCoord(), + stack); + entityitem.delayBeforeCanPickup = 10; + mte.getWorld() + .spawnEntityInWorld(entityitem); + } + } + } + + // endregion + + // region tool interactions + + /** + * Right click = change setup phase + * Shift+Right Click = change EIG Mode + */ + @Override + public void onScrewdriverRightClick(ForgeDirection side, EntityPlayer aPlayer, float aX, float aY, float aZ, + ItemStack aTool) { + if (aPlayer.isSneaking()) { + tryChangeMode(aPlayer); + } else { + tryChangeSetupPhase(aPlayer); + } + } + + /** + * Right-Clicking with wire cutters toggle no hydration mode. + */ + @Override + public boolean onWireCutterRightClick(ForgeDirection side, ForgeDirection wrenchingSide, EntityPlayer aPlayer, + float aX, float aY, float aZ, ItemStack aTool) { + this.tryChangeHumidityMode(aPlayer); + return true; + } + + // endregion tool interactions + + // region mode change standardisation + + /** + * Attempts to change the setup phase of the EIG to the next mode + * + * @param aPlayer The player to notify for success and errors + */ + private void tryChangeSetupPhase(EntityPlayer aPlayer) { + // TODO: Create l10n entries for the setup phase change messages. + if (this.mMaxProgresstime > 0) { + GT_Utility.sendChatToPlayer(aPlayer, "You can't enable/disable setup if the machine is working!"); + return; + } + this.setupPhase++; + if (this.setupPhase == 3) this.setupPhase = 0; + String phaseChangeMessage = "EIG is now running in "; + switch (this.setupPhase) { + case 0: + phaseChangeMessage += "operational mode."; + break; + case 1: + phaseChangeMessage += "seed input mode."; + break; + case 2: + phaseChangeMessage += "seed output mode."; + break; + default: + phaseChangeMessage += "an invalid mode please send us a ticket!"; + break; + } + this.updateSeedLimits(); + GT_Utility.sendChatToPlayer(aPlayer, phaseChangeMessage); + } + + /** + * Attempts to change the mode of the EIG to the next mode. + * + * @param aPlayer The player to notify of success and errors + */ + private void tryChangeMode(EntityPlayer aPlayer) { + // TODO: Create l10n entries for the mode change messages. + if (this.mMaxProgresstime > 0) { + GT_Utility.sendChatToPlayer(aPlayer, "You can't change mode if the machine is working!"); + return; + } + if (!this.buckets.isEmpty()) { + GT_Utility.sendChatToPlayer(aPlayer, "You can't change mode if there are seeds inside!"); + return; + } + this.mode = EIGModes.getNextMode(this.mode); + this.updateSeedLimits(); + GT_Utility.sendChatToPlayer(aPlayer, "Changed mode to: " + this.mode.getName()); + } + + /** + * Attempts to toggle the hydration mode of the EIG. + * + * @param aPlayer The player to notify for success and errors + */ + private void tryChangeHumidityMode(EntityPlayer aPlayer) { + // TODO: Create l10n entries for the humidity status interactions. + this.useNoHumidity = !this.useNoHumidity; + if (this.useNoHumidity) { + GT_Utility.sendChatToPlayer(aPlayer, "No Humidity mode enabled."); + } else { + GT_Utility.sendChatToPlayer(aPlayer, "No Humidity mode disabled."); + } + } + + // endregion mode change standardisation + + // region (de)serialisations + @Override public void saveNBTData(NBTTagCompound aNBT) { super.saveNBTData(aNBT); - aNBT.setInteger("EIG_MATH_VERSION", EIG_MATH_VERSION); - aNBT.setByte("glasTier", glasTier); - aNBT.setInteger("setupphase", setupphase); - aNBT.setBoolean("isIC2Mode", isIC2Mode); - aNBT.setBoolean("isNoHumidity", isNoHumidity); - aNBT.setInteger("mStorageSize", mStorage.size()); - for (int i = 0; i < mStorage.size(); i++) aNBT.setTag( - "mStorage." + i, - mStorage.get(i) - .toNBTTagCompound()); + aNBT.setInteger("version", NBT_REVISION); + aNBT.setByte("glassTier", this.glassTier); + aNBT.setInteger("setupPhase", this.setupPhase); + aNBT.setString("mode", this.mode.getName()); + aNBT.setBoolean("isNoHumidity", this.useNoHumidity); + NBTTagList bucketListNBT = new NBTTagList(); + for (EIGBucket b : this.buckets) { + bucketListNBT.appendTag(b.save()); + } + aNBT.setTag( + "progress", + this.dropTracker.intersect(this.guiDropTracker) + .save()); + aNBT.setTag("buckets", bucketListNBT); + } + + private static class EIGMigrationHolder { + + public final ItemStack seed; + public final ItemStack supportBlock; + public final boolean useNoHumidity; + public int count; + public boolean isValid = false; + + public EIGMigrationHolder(NBTTagCompound nbt) { + this.seed = readItemStackFromNBT(nbt.getCompoundTag("input")); + this.count = this.seed.stackSize; + this.seed.stackSize = 1; + this.supportBlock = nbt.hasKey("undercrop", 10) ? readItemStackFromNBT(nbt.getCompoundTag("undercrop")) + : null; + this.useNoHumidity = nbt.getBoolean("noHumidity"); + this.isValid = true; + } + + public String getKey() { + if (this.supportBlock == null) return seed.toString(); + return "(" + this.seed.toString() + "," + this.supportBlock + ")"; + } + } @Override public void loadNBTData(NBTTagCompound aNBT) { super.loadNBTData(aNBT); - oldVersion = aNBT.hasKey("EIG_MATH_VERSION") ? aNBT.getInteger("EIG_MATH_VERSION") : -1; - glasTier = aNBT.getByte("glasTier"); - setupphase = aNBT.getInteger("setupphase"); - isIC2Mode = aNBT.getBoolean("isIC2Mode"); - isNoHumidity = aNBT.getBoolean("isNoHumidity"); - for (int i = 0; i < aNBT.getInteger("mStorageSize"); i++) - mStorage.add(new GreenHouseSlot(aNBT.getCompoundTag("mStorage." + i))); + int revision = aNBT.hasKey("version", 3) ? aNBT.getInteger("version") : 0; + if (revision <= 0) { + // migrate old EIG with greenhouse slots to new Bucker mode and fix variable names + this.glassTier = aNBT.getByte("glasTier"); + this.setupPhase = aNBT.getInteger("setupphase"); + this.mode = aNBT.getBoolean("isIC2Mode") ? EIGModes.IC2 : EIGModes.Normal; + this.useNoHumidity = aNBT.getBoolean("isNoHumidity"); + // aggregate all seed types + HashMap toMigrate = new HashMap<>(); + for (int i = 0; i < aNBT.getInteger("mStorageSize"); i++) { + EIGMigrationHolder holder = new EIGMigrationHolder(aNBT.getCompoundTag("mStorage." + i)); + if (toMigrate.containsKey(holder.getKey())) { + toMigrate.get(holder.getKey()).count += holder.count; + } else { + toMigrate.put(holder.getKey(), holder); + } + } + + this.toMigrate = toMigrate.values(); + } else { + this.glassTier = aNBT.getByte("glassTier"); + this.setupPhase = aNBT.getInteger("setupPhase"); + this.mode = EIGModes.getModeFromName(aNBT.getString("mode")); + this.useNoHumidity = aNBT.getBoolean("isNoHumidity"); + this.mode.restoreBuckets(aNBT.getTagList("buckets", 10), this.buckets); + new EIGDropTable(aNBT.getTagList("progress", 10)).addTo(this.dropTracker); + } } + // endregion + + // region crop visuals rendering + @SideOnly(Side.CLIENT) public void spawnVisualCrops(World world, int x, int y, int z, int age) { CropRenderer crop = new CropRenderer(world, x, y, z, age); @@ -427,46 +632,129 @@ public class GT_MetaTileEntity_ExtremeIndustrialGreenhouse } } - @Override - public void construct(ItemStack itemStack, boolean b) { - buildPiece(STRUCTURE_PIECE_MAIN, itemStack, b, 2, 5, 0); + // endregion crop visuals rendering + + /** + * Calculates the total amount of seeds in the EIG + * + * @return The number of seeds in the EIG. + */ + private int getTotalSeedCount() { + // null check is to prevent a occasional weird NPE from MUI + return this.buckets.parallelStream() + .reduce(0, (b, t) -> b + t.getSeedCount(), Integer::sum); } - private void updateMaxSlots() { - int tier = getVoltageTier(); - if (tier < (isIC2Mode ? (EIG_BALANCE_IC2_ACCELERATOR_TIER + 1) : 4)) mMaxSlots = 0; - else if (isIC2Mode) mMaxSlots = 4 << (2 * (tier - (EIG_BALANCE_IC2_ACCELERATOR_TIER + 1))); - else mMaxSlots = 1 << (tier - 4); + /** + * Updates the max seed counts of the machine + */ + private void updateSeedLimits() { + this.maxSeedTypes = this.mode.getSlotCount(getVoltageTier()); + this.maxSeedCount = this.maxSeedTypes * this.mode.getSeedCapacityPerSlot(); + } + + /** + * Attempts to drain the multi of a given fluid, will only return true if all fluid is consumed. + * + * @param toConsume A fluid stack of the fluid to consume. + * @param drainPartial True to allow partial consumption. + * @return True when all the fluid has been consumed. + */ + private boolean tryDrain(FluidStack toConsume, boolean drainPartial) { + // Nothing to consume = success I guess? + if (toConsume == null || toConsume.amount <= 0) return true; + // TODO: improve fluid draining logic. + List fluids = this.getStoredFluids(); + List fluidsToUse = new ArrayList<>(fluids.size()); + int remaining = toConsume.amount; + for (FluidStack fluid : fluids) { + if (fluid.isFluidEqual(toConsume)) { + remaining -= fluid.amount; + fluidsToUse.add(fluid); + if (remaining <= 0) break; + } + } + if (!drainPartial && remaining > 0 && !debug) return false; + boolean success = remaining <= 0; + remaining = toConsume.amount - Math.max(0, remaining); + for (FluidStack fluid : fluidsToUse) { + int used = Math.min(remaining, fluid.amount); + fluid.amount -= used; + remaining -= used; + } + return success; + } + + /** + * Checks if a stack contains an item that can be used as fertilizer + * + * @param item A stack of item to validate + * @return True if the item can be consumed as fertilizer. + */ + public static boolean isFertilizer(ItemStack item) { + if (item == null || item.stackSize <= 0) return false; + for (ItemStack fert : FERTILIZER_ITEM_LIST) { + if (GT_Utility.areStacksEqual(item, fert)) return true; + } + return false; + } + + private boolean tryEmptyBucket(EIGBucket bucket) { + // check if it's already empty + if (bucket.getSeedCount() <= 0) return true; + + // check if we have an ME output bus to output to. + for (GT_MetaTileEntity_Hatch_OutputBus tHatch : filterValidMTEs(mOutputBusses)) { + if (!(tHatch instanceof GT_MetaTileEntity_Hatch_OutputBus_ME)) continue; + for (ItemStack stack : bucket.tryRemoveSeed(bucket.getSeedCount(), false)) { + ((GT_MetaTileEntity_Hatch_OutputBus_ME) tHatch).store(stack); + } + return true; + } + + // Else attempt to empty the bucket while not voiding anything. + ItemStack[] simulated = bucket.tryRemoveSeed(1, true); + VoidProtectionHelper helper = new VoidProtectionHelper().setMachine(this, true, false) + .setItemOutputs(simulated) + .setMaxParallel(bucket.getSeedCount()) + .build(); + if (helper.getMaxParallel() > 0) { + for (ItemStack toOutput : bucket.tryRemoveSeed(helper.getMaxParallel(), false)) { + for (GT_MetaTileEntity_Hatch_OutputBus tHatch : filterValidMTEs(mOutputBusses)) { + if (tHatch.storeAll(toOutput)) break; + } + } + } + return bucket.getSeedCount() <= 0; } @Override @NotNull public CheckRecipeResult checkProcessing() { int tier = getVoltageTier(); - updateMaxSlots(); - - if (oldVersion != EIG_MATH_VERSION) { - for (GreenHouseSlot slot : mStorage) slot.recalculate(this, getBaseMetaTileEntity().getWorld()); - oldVersion = EIG_MATH_VERSION; - } + updateSeedLimits(); - if (setupphase > 0) { - if ((mStorage.size() >= mMaxSlots && setupphase == 1) || (mStorage.isEmpty() && setupphase == 2)) + if (setupPhase > 0) { + if ((buckets.size() >= maxSeedTypes && setupPhase == 1) || (buckets.isEmpty() && setupPhase == 2)) return CheckRecipeResultRegistry.NO_RECIPE; - if (setupphase == 1) { + if (setupPhase == 1) { List inputs = getStoredInputs(); for (ItemStack input : inputs) { addCrop(input); - if (mStorage.size() >= mMaxSlots) break; + if (buckets.size() >= maxSeedTypes) break; + } + } else if (setupPhase == 2) { + for (Iterator iterator = this.buckets.iterator(); iterator.hasNext();) { + EIGBucket bucket = iterator.next(); + if (tryEmptyBucket(bucket)) { + iterator.remove(); + } else { + this.mMaxProgresstime = 20; + this.lEUt = 0; + return CheckRecipeResultRegistry.ITEM_OUTPUT_FULL; + } } - } else if (setupphase == 2) { - tryOutputAll(mStorage, s -> { - ArrayList l = new ArrayList<>(2); - l.add(((GreenHouseSlot) s).input.copy()); - if (((GreenHouseSlot) s).undercrop != null) l.add(((GreenHouseSlot) s).undercrop.copy()); - return l; - }); } this.updateSlots(); @@ -476,49 +764,56 @@ public class GT_MetaTileEntity_ExtremeIndustrialGreenhouse this.mEfficiencyIncrease = 10000; return CheckRecipeResultRegistry.SUCCESSFUL; } - if (mStorage.size() > mMaxSlots) return SimpleCheckRecipeResult.ofFailure("EIG_slotoverflow"); - if (mStorage.isEmpty()) return CheckRecipeResultRegistry.NO_RECIPE; - - waterusage = 0; - weedexusage = 0; - for (GreenHouseSlot s : mStorage) waterusage += s.input.stackSize; - if (waterusage >= EIG_CROP_LIMIT_FOR_WEEDEX9000_REQUIREMENT) weedexusage = waterusage; - waterusage *= 1000; - - List fluids = mInputHatches; - List fluidsToUse = new ArrayList<>(fluids.size()); - int watercheck = waterusage; - FluidStack waterStack = new FluidStack(FluidRegistry.WATER, 1); - for (GT_MetaTileEntity_Hatch_Input i : fluids) { - if (!i.isValid()) continue; - if (i instanceof GT_MetaTileEntity_Hatch_MultiInput) { - int amount = ((GT_MetaTileEntity_Hatch_MultiInput) i).getFluidAmount(waterStack); - if (amount == 0) continue; - watercheck -= amount; - } else { - FluidStack stack = i.getDrainableStack(); - if (stack == null) continue; - if (!stack.isFluidEqual(waterStack)) continue; - if (stack.amount <= 0) continue; - watercheck -= stack.amount; + if (this.maxSeedTypes < this.buckets.size()) { + return SimpleCheckRecipeResult.ofFailure("EIG_slotoverflow"); + } + int seedCount = this.getTotalSeedCount(); + if (this.maxSeedCount < seedCount) { + return SimpleCheckRecipeResult.ofFailure("EIG_seedOverflow"); + } + + // Kick out bad buckets. + for (Iterator iterator = this.buckets.iterator(); iterator.hasNext();) { + EIGBucket bucket = iterator.next(); + if (bucket.isValid() || bucket.revalidate(this)) continue; + // attempt to empty the bucket + tryEmptyBucket(bucket); + // remove empty bucket and attempt to revalidate invalid buckets + if (bucket.getSeedCount() <= 0) { + iterator.remove(); } - fluidsToUse.add(i); - if (watercheck <= 0) break; } - if (watercheck > 0 && !debug) return SimpleCheckRecipeResult.ofFailure("EIG_missingwater"); - watercheck = waterusage; - for (GT_MetaTileEntity_Hatch_Input i : fluidsToUse) { - int used = i.drain(watercheck, true).amount; - watercheck -= used; + + if (this.buckets.isEmpty()) return CheckRecipeResultRegistry.NO_RECIPE; + + // Compute the Weed-EX and water requirements, + // TODO: We only really need to update water usage and WeedEX usage when adding seeds or when loading NBT. + this.waterUsage = seedCount * 1000; + this.weedEXUsage = (seedCount >= EIG_BALANCE_WEED_EX_USAGE_BEGINS_AT ? seedCount : 0) + * this.mode.getWeedEXMultiplier(); + + // Consume water, fail if we don't have enough + if (!this.tryDrain(new FluidStack(FluidRegistry.WATER, this.waterUsage), false)) { + return SimpleCheckRecipeResult.ofFailure("EIG_missingwater"); } - // weedex - if (weedexusage > 0 && !this.depleteInput(new FluidStack(weedex, isIC2Mode ? weedexusage * 5 : weedexusage))) { + // Consume weed ex, if there isn't enough we consume what's there but don't fail + if (weedEXUsage > 0 && !this.tryDrain(new FluidStack(WEEDEX_FLUID, this.weedEXUsage), true)) { IGregTechTileEntity baseMTE = this.getBaseMetaTileEntity(); - int toKill = baseMTE.getRandomNumber((int) ((double) weedexusage * 0.02d) + 1); - while (toKill > 0) { - GreenHouseSlot removed = mStorage.remove(baseMTE.getRandomNumber(mStorage.size())); - toKill -= removed.input.stackSize; + // Cap seed murder to the Weed EX limit, no more senseless murder of bystanders + int killLimit = (seedCount - EIG_BALANCE_WEED_EX_USAGE_BEGINS_AT + 1); + int toKill = Math.min(killLimit, baseMTE.getRandomNumber((int) ((double) seedCount * 0.02d) + 1)); + if (toKill > 0) { + for (Iterator iterator = this.buckets.iterator(); iterator.hasNext();) { + EIGBucket bucket = iterator.next(); + ItemStack[] removed = bucket.tryRemoveSeed(toKill, false); + if (removed == null || removed[0].stackSize <= 0) continue; + toKill -= removed[0].stackSize; + // if bucket is empty, yeet it out. + if (bucket.getSeedCount() <= 0) iterator.remove(); + // if we are out of crops to kill we can just leave + if (toKill <= 0) break; + } } } @@ -527,74 +822,96 @@ public class GT_MetaTileEntity_ExtremeIndustrialGreenhouse // IC2 +10% per fertilizer per crop per operation // NORMAL +200% per fertilizer per crop per operation - int boost = 0; - int maxboost = 0; - for (GreenHouseSlot s : mStorage) maxboost += s.input.stackSize * (isIC2Mode ? 40 : 2); + int consumedFertilizer = 0; + int maxFertilizerToConsume = 0; + for (EIGBucket bucket : this.buckets) + maxFertilizerToConsume += bucket.getSeedCount() * this.mode.getMaxFertilizerUsagePerSeed(); ArrayList inputs = getStoredInputs(); for (ItemStack i : inputs) { - if ((i.getItem() == Items.dye && i.getItemDamage() == 15) - || (forestryfertilizer != null && (i.getItem() == forestryfertilizer)) - || (GT_Utility.areStacksEqual(i, Ic2Items.fertilizer))) { - int used = Math.min(i.stackSize, maxboost - boost); + if (isFertilizer(i)) { + int used = Math.min(i.stackSize, maxFertilizerToConsume - consumedFertilizer); i.stackSize -= used; - boost += used; + consumedFertilizer += used; } - if (boost == maxboost) break; + if (consumedFertilizer == maxFertilizerToConsume) break; } + double multiplier = 1.d + + (((double) consumedFertilizer / (double) maxFertilizerToConsume) * EIG_BALANCE_MAX_FERTILIZER_BOOST); - double multiplier = 1.d + (((double) boost / (double) maxboost) * 4d); - - if (isIC2Mode) { - if (glasTier < (EIG_BALANCE_IC2_ACCELERATOR_TIER + 1)) + // compute drops based on the drop tracker + this.guiDropTracker = new EIGDropTable(); + if (this.mode == EIGModes.IC2) { + if (glassTier < (EIG_BALANCE_IC2_ACCELERATOR_TIER + 1)) return SimpleCheckRecipeResult.ofFailure("EIG_ic2glass"); this.mMaxProgresstime = 100; - List outputs = new ArrayList<>(); - for (int i = 0; i < Math.min(mMaxSlots, mStorage.size()); i++) outputs.addAll( - mStorage.get(i) - .getIC2Drops( - this, - ((double) this.mMaxProgresstime * (1 << EIG_BALANCE_IC2_ACCELERATOR_TIER)) * multiplier)); - this.mOutputItems = outputs.toArray(new ItemStack[0]); - } else { + // determine the amount of time we are simulating on the seed. + double timeElapsed = ((double) this.mMaxProgresstime * (1 << EIG_BALANCE_IC2_ACCELERATOR_TIER)); + // Add drops to the drop tracker for each seed bucket. + for (EIGBucket bucket : this.buckets) { + bucket.addProgress(timeElapsed * multiplier, this.guiDropTracker); + } + } else if (this.mode == EIGModes.Normal) { this.mMaxProgresstime = Math.max(20, 100 / (tier - 3)); // Min 1 s - List outputs = new ArrayList<>(); - for (int i = 0; i < Math.min(mMaxSlots, mStorage.size()); i++) { - for (ItemStack drop : mStorage.get(i) - .getDrops()) { - ItemStack s = drop.copy(); - s.stackSize = (int) ((double) s.stackSize * multiplier); - outputs.add(s); - } + for (EIGBucket bucket : this.buckets) { + bucket.addProgress(multiplier, this.guiDropTracker); } - this.mOutputItems = outputs.toArray(new ItemStack[0]); } - this.lEUt = -(int) ((double) GT_Values.V[tier] * 0.99d); + + this.guiDropTracker.addTo(this.dropTracker, multiplier); + this.mOutputItems = this.dropTracker.getDrops(); + + // consume power + this.lEUt = -(long) ((double) GT_Values.V[tier] * 0.99d); this.mEfficiency = (10000 - (getIdealStatus() - getRepairStatus()) * 1000); this.mEfficiencyIncrease = 10000; this.updateSlots(); return CheckRecipeResultRegistry.SUCCESSFUL; } - @Override - public boolean checkMachine(IGregTechTileEntity iGregTechTileEntity, ItemStack itemStack) { - mCasing = 0; - glasTier = 0; - if (debug) glasTier = 8; - - if (!checkPiece(STRUCTURE_PIECE_MAIN, 2, 5, 0)) return false; - - if (this.glasTier < 8 && !this.mEnergyHatches.isEmpty()) - for (GT_MetaTileEntity_Hatch_Energy hatchEnergy : this.mEnergyHatches) - if (this.glasTier < hatchEnergy.mTier) return false; + private ItemStack addCrop(ItemStack input) { + return addCrop(input, false) ? input : null; + } - boolean valid = this.mMaintenanceHatches.size() == 1 && !this.mEnergyHatches.isEmpty() && this.mCasing >= 70; + /** + * Adds a seed to the EIG + * + * @param input The item to add to the EIG. + * @param simulate Set to true to not actually consume any input. + * @return True if all items were consumed + */ + private boolean addCrop(ItemStack input, boolean simulate) { + // Nothing to add = success since technically nothing should have changed? + if (input == null || input.stackSize <= 0) return true; + + // For safety's sake copy the input if we are simulating to make sure we aren't modifying it + if (simulate) input = input.copy(); + + // Cap input count to current seed max + int addCap = Math.min(input.stackSize, this.maxSeedCount - this.getTotalSeedCount()); + if (addCap <= 0) return false; + + // Attempt to find a compatible bucket that already exists + for (EIGBucket bucket : this.buckets) { + int consumed = bucket.tryAddSeed(this, input, addCap, simulate); + if (consumed <= 0) continue; + return input.stackSize <= 0; + } - if (valid) updateMaxSlots(); + // Check if we have space for a new bucket + if (this.maxSeedTypes <= this.buckets.size()) { + return false; + } - return valid; + // try creating a new bucket, this only returns valid buckets. + EIGBucket bucket = this.mode.tryCreateNewBucket(this, input, addCap, simulate); + if (bucket == null) return false; + this.buckets.add(bucket); + return input.stackSize <= 0; } + // region ui + private static final UIInfo GreenhouseUI = createKTMetaTileEntityUI( KT_ModulaUIContainer_ExtremeIndustrialGreenhouse::new); @@ -630,18 +947,17 @@ public class GT_MetaTileEntity_ExtremeIndustrialGreenhouse if (aStack == null) return super.transferStackInSlot(aPlayer, aSlotIndex); GT_MetaTileEntity_ExtremeIndustrialGreenhouse mte = parent.get(); if (mte == null) return super.transferStackInSlot(aPlayer, aSlotIndex); - if (mte.mStorage.size() >= mte.mMaxSlots) return super.transferStackInSlot(aPlayer, aSlotIndex); + // if (mte.buckets.size() >= mte.maxSeedTypes) return super.transferStackInSlot(aPlayer, aSlotIndex); if (mte.mMaxProgresstime > 0) { GT_Utility.sendChatToPlayer(aPlayer, EnumChatFormatting.RED + "Can't insert while running !"); return super.transferStackInSlot(aPlayer, aSlotIndex); } - if (mte.addCrop(aStack) != null) { - if (aStack.stackSize == 0) s.putStack(null); - else s.putStack(aStack); - detectAndSendChanges(); - return null; - } - return super.transferStackInSlot(aPlayer, aSlotIndex); + + mte.addCrop(aStack); + if (aStack.stackSize <= 0) s.putStack(null); + else s.putStack(aStack); + detectAndSendChanges(); + return null; } } @@ -660,38 +976,32 @@ public class GT_MetaTileEntity_ExtremeIndustrialGreenhouse .setSize(16, 16)); } - DynamicInventory dynamicInventory = new DynamicInventory<>( + EIGDynamicInventory dynamicInventory = new EIGDynamicInventory<>( 128, 60, - () -> mMaxSlots, - mStorage, - s -> s.input).allowInventoryInjection(this::addCrop) - .allowInventoryExtraction(mStorage::remove) - .allowInventoryReplace((i, stack) -> { - if (!isIC2Mode) { - GreenHouseSlot slot = mStorage.get(i); - if (GT_Utility.areStacksEqual(stack, slot.input)) { - if (slot.input.stackSize < 64) { - slot.addAll( - this.getBaseMetaTileEntity() - .getWorld(), - stack); - return stack; - } - return null; - } - if (!addCrop(stack, i, true)) return null; - slot = mStorage.remove(i); - addCrop(stack, i, false); - return slot.input; - } else { - if (stack.stackSize != 1) return null; - if (!addCrop(stack, i, true)) return null; - GreenHouseSlot slot = mStorage.remove(i); - addCrop(stack, i, false); - return slot.input; + () -> this.maxSeedTypes, + () -> this.maxSeedCount, + this.buckets::size, + this::getTotalSeedCount, + this.buckets, + EIGBucket::getSeedStack).allowInventoryInjection(this::addCrop) + .allowInventoryExtraction((bucket, player) -> { + if (bucket == null) return null; + int maxRemove = bucket.getSeedStack() + .getMaxStackSize(); + ItemStack[] outputs = bucket.tryRemoveSeed(maxRemove, false); + if (outputs == null || outputs.length <= 0) return null; + ItemStack ret = outputs[0]; + for (int i = 1; i < outputs.length; i++) { + ItemStack suppertItem = outputs[i]; + if (!player.inventory.addItemStackToInventory(suppertItem)) { + player.entityDropItem(suppertItem, 0.f); + } ; } + if (bucket.getSeedCount() <= 0) this.buckets.remove(bucket); + return ret; }) + // TODO: re-add allow inventory replace? .setEnabled(() -> this.mMaxProgresstime == 0); @Override @@ -712,8 +1022,8 @@ public class GT_MetaTileEntity_ExtremeIndustrialGreenhouse builder.widget( dynamicInventory.asWidget(builder, buildContext) .setPos(10, 16) - .setBackground(new Rectangle().setColor(Color.rgb(163, 163, 198))) .setEnabled(w -> isInInventory)); + builder.widget( new CycleButtonWidget().setToggle(() -> isInInventory, i -> isInInventory = i) .setTextureGetter(i -> i == 0 ? new Text("Inventory") : new Text("Status")) @@ -754,20 +1064,10 @@ public class GT_MetaTileEntity_ExtremeIndustrialGreenhouse .widget( new Column().widget( new CycleButtonWidget().setLength(3) - .setGetter(() -> setupphase) + .setGetter(() -> this.setupPhase) .setSetter(val -> { if (!(player instanceof EntityPlayerMP)) return; - if (this.mMaxProgresstime > 0) { - GT_Utility.sendChatToPlayer( - player, - "You can't enable/disable setup if the machine is working!"); - return; - } - this.setupphase = val; - GT_Utility.sendChatToPlayer( - player, - "EIG is now running in " + (this.setupphase == 1 ? "setup mode (input)." - : (this.setupphase == 2 ? "setup mode (output)." : "normal operation."))); + tryChangeSetupPhase(player); }) .addTooltip(0, new Text("Operating").color(Color.GREEN.dark(3))) .addTooltip(1, new Text("Input").color(Color.YELLOW.dark(3))) @@ -786,25 +1086,10 @@ public class GT_MetaTileEntity_ExtremeIndustrialGreenhouse .addTooltip("Setup mode")) .widget( new CycleButtonWidget().setLength(2) - .setGetter(() -> isIC2Mode ? 1 : 0) + .setGetter(() -> this.mode.getUIIndex()) .setSetter(val -> { if (!(player instanceof EntityPlayerMP)) return; - if (this.mMaxProgresstime > 0) { - GT_Utility.sendChatToPlayer( - player, - "You can't change IC2 mode if the machine is working!"); - return; - } - if (!mStorage.isEmpty()) { - GT_Utility.sendChatToPlayer( - player, - "You can't change IC2 mode if there are seeds inside!"); - return; - } - this.isIC2Mode = val == 1; - GT_Utility.sendChatToPlayer( - player, - "IC2 mode is now " + (this.isIC2Mode ? "enabled" : "disabled.")); + tryChangeMode(player); }) .addTooltip(0, new Text("Disabled").color(Color.RED.dark(3))) .addTooltip(1, new Text("Enabled").color(Color.GREEN.dark(3))) @@ -820,11 +1105,10 @@ public class GT_MetaTileEntity_ExtremeIndustrialGreenhouse .addTooltip("IC2 mode")) .widget( new CycleButtonWidget().setLength(2) - .setGetter(() -> isNoHumidity ? 1 : 0) + .setGetter(() -> useNoHumidity ? 1 : 0) .setSetter(val -> { if (!(player instanceof EntityPlayerMP)) return; - isNoHumidity = val == 1; - GT_Utility.sendChatToPlayer(player, "Give incoming crops no humidity " + isNoHumidity); + this.tryChangeHumidityMode(player); }) .addTooltip(0, new Text("Disabled").color(Color.RED.dark(3))) .addTooltip(1, new Text("Enabled").color(Color.GREEN.dark(3))) @@ -855,21 +1139,25 @@ public class GT_MetaTileEntity_ExtremeIndustrialGreenhouse return builder.build(); } - private HashMap GUIDropProgress = new HashMap<>(); - @Override protected String generateCurrentRecipeInfoString() { - if (!isIC2Mode) return super.generateCurrentRecipeInfoString(); StringBuilder ret = new StringBuilder(EnumChatFormatting.WHITE + "Progress: ") - .append(String.format("%,.2f", (double) mProgresstime / 20)) + .append(String.format("%,.2f", (double) this.mProgresstime / 20)) .append("s / ") - .append(String.format("%,.2f", (double) mMaxProgresstime / 20)) + .append(String.format("%,.2f", (double) this.mMaxProgresstime / 20)) .append("s (") - .append(String.format("%,.1f", (double) mProgresstime / mMaxProgresstime * 100)) + .append(String.format("%,.1f", (double) this.mProgresstime / this.mMaxProgresstime * 100)) .append("%)\n"); - for (Map.Entry drop : GUIDropProgress.entrySet()) { - int outputSize = Arrays.stream(mOutputItems) + for (Map.Entry drop : this.synchedGUIDropTracker.entrySet() + .stream() + .sorted( + Comparator.comparing( + a -> a.getKey() + .toString() + .toLowerCase())) + .collect(Collectors.toList())) { + int outputSize = Arrays.stream(this.mOutputItems) .filter(s -> s.isItemEqual(drop.getKey())) .mapToInt(i -> i.stackSize) .sum(); @@ -892,26 +1180,24 @@ public class GT_MetaTileEntity_ExtremeIndustrialGreenhouse (double) outputSize / (mMaxProgresstime / 20))); } } - return ret.toString(); } @Override protected void drawTexts(DynamicPositionedColumn screenElements, SlotWidget inventorySlot) { - screenElements.widget(new FakeSyncWidget.BooleanSyncer(() -> isIC2Mode, b -> isIC2Mode = b)); + screenElements.widget( + new FakeSyncWidget.BooleanSyncer( + () -> this.mode == EIGModes.IC2, + b -> this.mode = b ? EIGModes.IC2 : EIGModes.Normal)); screenElements.widget(new FakeSyncWidget<>(() -> { HashMap ret = new HashMap<>(); - HashMap dropProgress = new HashMap<>(); - for (Map.Entry drop : dropprogress.entrySet()) { - dropProgress.merge(drop.getKey(), drop.getValue(), Double::sum); + for (Map.Entry drop : this.guiDropTracker.entrySet()) { + ret.merge(drop.getKey(), drop.getValue(), Double::sum); } - for (Map.Entry drop : dropProgress.entrySet()) { - ret.put(GreenHouseSlot.dropstacks.get(drop.getKey()), drop.getValue()); - } return ret; - }, h -> GUIDropProgress = h, (buffer, h) -> { + }, h -> this.synchedGUIDropTracker = h, (buffer, h) -> { buffer.writeVarIntToBuffer(h.size()); for (Map.Entry itemStackDoubleEntry : h.entrySet()) { try { @@ -941,33 +1227,28 @@ public class GT_MetaTileEntity_ExtremeIndustrialGreenhouse List info = new ArrayList<>( Arrays.asList( "Running in mode: " + EnumChatFormatting.GREEN - + (setupphase == 0 ? (isIC2Mode ? "IC2 crops" : "Normal crops") - : ("Setup mode " + (setupphase == 1 ? "(input)" : "(output)"))) + + (this.setupPhase == 0 ? this.mode.getName() + : ("Setup mode " + (this.setupPhase == 1 ? "(input)" : "(output)"))) + EnumChatFormatting.RESET, - "Uses " + waterusage + "L/operation of water", - "Uses " + weedexusage + "L/second of Weed-EX 9000", - "Max slots: " + EnumChatFormatting.GREEN + this.mMaxSlots + EnumChatFormatting.RESET, - "Used slots: " + ((mStorage.size() > mMaxSlots) ? EnumChatFormatting.RED : EnumChatFormatting.GREEN) - + this.mStorage.size() + "Uses " + waterUsage + "L/operation of water", + "Uses " + weedEXUsage + "L/second of Weed-EX 9000", + "Max slots: " + EnumChatFormatting.GREEN + this.maxSeedTypes + EnumChatFormatting.RESET, + "Used slots: " + + ((this.buckets.size() > maxSeedTypes) ? EnumChatFormatting.RED : EnumChatFormatting.GREEN) + + this.buckets.size() + EnumChatFormatting.RESET)); - HashMap storageList = new HashMap<>(); - for (GreenHouseSlot greenHouseSlot : mStorage) { - if (!greenHouseSlot.isValid) continue; - StringBuilder a = new StringBuilder( - EnumChatFormatting.GREEN + "x" - + greenHouseSlot.input.stackSize - + " " -