diff options
Diffstat (limited to 'src/main/java/kubatech/tileentity/gregtech')
4 files changed, 3829 insertions, 0 deletions
diff --git a/src/main/java/kubatech/tileentity/gregtech/multiblock/GT_MetaTileEntity_DEFusionCrafter.java b/src/main/java/kubatech/tileentity/gregtech/multiblock/GT_MetaTileEntity_DEFusionCrafter.java new file mode 100644 index 0000000000..f0579bf054 --- /dev/null +++ b/src/main/java/kubatech/tileentity/gregtech/multiblock/GT_MetaTileEntity_DEFusionCrafter.java @@ -0,0 +1,281 @@ +package kubatech.tileentity.gregtech.multiblock; + +import static com.gtnewhorizon.structurelib.structure.StructureUtility.ofBlock; +import static com.gtnewhorizon.structurelib.structure.StructureUtility.ofBlocksTiered; +import static com.gtnewhorizon.structurelib.structure.StructureUtility.onElementPass; +import static com.gtnewhorizon.structurelib.structure.StructureUtility.transpose; +import static gregtech.api.enums.GT_HatchElement.Energy; +import static gregtech.api.enums.GT_HatchElement.InputBus; +import static gregtech.api.enums.GT_HatchElement.InputHatch; +import static gregtech.api.enums.GT_HatchElement.Maintenance; +import static gregtech.api.enums.GT_HatchElement.OutputBus; +import static gregtech.api.enums.GT_HatchElement.OutputHatch; +import static gregtech.api.enums.Textures.BlockIcons.MACHINE_CASING_MAGIC; +import static gregtech.api.enums.Textures.BlockIcons.MACHINE_CASING_MAGIC_ACTIVE; +import static gregtech.api.enums.Textures.BlockIcons.MACHINE_CASING_MAGIC_ACTIVE_GLOW; +import static gregtech.api.enums.Textures.BlockIcons.MACHINE_CASING_MAGIC_GLOW; +import static gregtech.api.enums.Textures.BlockIcons.OVERLAY_TELEPORTER; +import static gregtech.api.enums.Textures.BlockIcons.OVERLAY_TELEPORTER_ACTIVE; +import static gregtech.api.enums.Textures.BlockIcons.OVERLAY_TELEPORTER_ACTIVE_GLOW; +import static gregtech.api.enums.Textures.BlockIcons.OVERLAY_TELEPORTER_GLOW; +import static gregtech.api.util.GT_StructureUtility.buildHatchAdder; +import static kubatech.api.Variables.StructureHologram; +import static kubatech.api.Variables.buildAuthorList; + +import java.util.Arrays; +import java.util.List; + +import net.minecraft.block.Block; +import net.minecraft.item.ItemStack; +import net.minecraftforge.common.util.ForgeDirection; + +import org.apache.commons.lang3.tuple.Pair; +import org.jetbrains.annotations.NotNull; + +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 gregtech.api.GregTech_API; +import gregtech.api.interfaces.ITexture; +import gregtech.api.interfaces.metatileentity.IMetaTileEntity; +import gregtech.api.interfaces.tileentity.IGregTechTileEntity; +import gregtech.api.logic.ProcessingLogic; +import gregtech.api.recipe.RecipeMap; +import gregtech.api.recipe.check.CheckRecipeResult; +import gregtech.api.recipe.check.CheckRecipeResultRegistry; +import gregtech.api.render.TextureFactory; +import gregtech.api.util.GT_Multiblock_Tooltip_Builder; +import gregtech.api.util.GT_OverclockCalculator; +import gregtech.api.util.GT_Recipe; +import kubatech.Tags; +import kubatech.api.implementations.KubaTechGTMultiBlockBase; +import kubatech.loaders.BlockLoader; +import kubatech.loaders.DEFCRecipes; + +public class GT_MetaTileEntity_DEFusionCrafter extends KubaTechGTMultiBlockBase<GT_MetaTileEntity_DEFusionCrafter> + implements ISurvivalConstructable { + + private static final int CASING_INDEX = (1 << 7) + (15 + 48); + private int mTierCasing = 0; + private int mFusionTierCasing = 0; + private int mCasing = 0; + + @SuppressWarnings("unused") + public GT_MetaTileEntity_DEFusionCrafter(int aID, String aName, String aNameRegional) { + super(aID, aName, aNameRegional); + } + + public GT_MetaTileEntity_DEFusionCrafter(String aName) { + super(aName); + } + + @Override + public IMetaTileEntity newMetaEntity(IGregTechTileEntity aTileEntity) { + return new GT_MetaTileEntity_DEFusionCrafter(mName); + } + + private static final String STRUCTURE_PIECE_MAIN = "main"; + private static final List<Pair<Block, Integer>> fusionCasingTiers = Arrays + .asList(Pair.of(GregTech_API.sBlockCasings4, 6), Pair.of(GregTech_API.sBlockCasings4, 8)); + private static final List<Pair<Block, Integer>> coreTiers = Arrays.asList( + Pair.of(BlockLoader.defcCasingBlock, 8), + Pair.of(BlockLoader.defcCasingBlock, 9), + Pair.of(BlockLoader.defcCasingBlock, 10), + Pair.of(BlockLoader.defcCasingBlock, 11), + Pair.of(BlockLoader.defcCasingBlock, 12)); + private static final IStructureDefinition<GT_MetaTileEntity_DEFusionCrafter> STRUCTURE_DEFINITION = StructureDefinition + .<GT_MetaTileEntity_DEFusionCrafter>builder() + .addShape( + STRUCTURE_PIECE_MAIN, + transpose( + new String[][] { // spotless:off + { "nnnnn", "nnnnn", "nnnnn", "nnnnn", "nnnnn" }, + { " ", " F ", " FfF ", " F ", " " }, + { " ", " F ", " FfF ", " F ", " " }, + { "RRRRR", "R F R", "RFfFR", "R F R", "RRRRR" }, + { " ", " F ", " FfF ", " F ", " " }, + { " ", " F ", " FfF ", " F ", " " }, + { "RRRRR", "R F R", "RFfFR", "R F R", "RRRRR" }, + { " ", " F ", " FfF ", " F ", " " }, + { " ", " F ", " FfF ", " F ", " " }, + { "NN~NN", "NNNNN", "NNNNN", "NNNNN", "NNNNN" } + })) // spotless:on + .addElement( + 'N', + buildHatchAdder(GT_MetaTileEntity_DEFusionCrafter.class) + .atLeast(InputBus, InputHatch, OutputBus, OutputHatch, Energy, Maintenance) + .casingIndex(CASING_INDEX) + .dot(1) + .buildAndChain(onElementPass(e -> e.mCasing++, ofBlock(BlockLoader.defcCasingBlock, 7)))) + .addElement('n', onElementPass(e -> e.mCasing++, ofBlock(BlockLoader.defcCasingBlock, 7))) + .addElement('f', ofBlock(GregTech_API.sBlockCasings4, 7)) + .addElement('F', ofBlocksTiered((Block b, int m) -> { + if (b != GregTech_API.sBlockCasings4 || (m != 6 && m != 8)) return -2; + return m == 6 ? 1 : 2; + }, fusionCasingTiers, -1, (e, i) -> e.mFusionTierCasing = i, e -> e.mFusionTierCasing)) + .addElement('R', ofBlocksTiered((Block b, int m) -> { + if (b != BlockLoader.defcCasingBlock || m < 8 || m > 12) return -2; + return m - 7; + }, coreTiers, -1, (e, i) -> e.mTierCasing = i, e -> e.mTierCasing)) + .build(); + + @Override + public IStructureDefinition<GT_MetaTileEntity_DEFusionCrafter> getStructureDefinition() { + return STRUCTURE_DEFINITION; + } + + public boolean checkMachine(IGregTechTileEntity aBaseMetaTileEntity, ItemStack aStack) { + mCasing = 0; + mTierCasing = -1; + mFusionTierCasing = -1; + if (!checkPiece(STRUCTURE_PIECE_MAIN, 2, 9, 0)) return false; + if (mCasing < 19) return false; + if (mTierCasing == -2 || mFusionTierCasing == -2) return false; + if (mTierCasing > 3 && mFusionTierCasing < 2) return false; + return mMaintenanceHatches.size() == 1; + } + + @Override + protected GT_Multiblock_Tooltip_Builder createTooltip() { + GT_Multiblock_Tooltip_Builder tt = new GT_Multiblock_Tooltip_Builder(); + tt.addMachineType("Fusion Crafter") + .addInfo("Controller Block for the Draconic Evolution Fusion Crafter") + .addInfo(buildAuthorList("kuba6000", "Prometheus0000")) + .addInfo("Machine can be overclocked by using casings above the recipe tier:") + .addInfo("Recipe time is divided by number of tiers above the recipe") + .addInfo("Normal EU OC still applies !") + .addInfo(StructureHologram) + .addSeparator() + .beginStructureBlock(5, 10, 5, false) + .addController("Front bottom center") + .addCasingInfoMin("Naquadah Alloy Fusion Casing", 19, false) + .addOtherStructurePart("Fusion Coil Block", "Center pillar") + .addOtherStructurePart("Fusion Machine Casing", "Touching Fusion Coil Block at every side") + .addOtherStructurePart("Tiered Fusion Casing", "Rings (5x5 hollow) at layer 4 and 7") + .addStructureInfo("Bloody Ichorium for tier 1, Draconium for tier 2, etc") + .addStructureInfo("To use tier 3 + you have to use fusion casing MK II") + .addInputBus("Any bottom casing", 1) + .addInputHatch("Any bottom casing", 1) + .addOutputBus("Any bottom casing", 1) + .addOutputHatch("Any bottom casing", 1) + .addEnergyHatch("Any bottom casing", 1) + .addMaintenanceHatch("Any bottom casing", 1) + .toolTipFinisher(Tags.MODNAME); + return tt; + } + + public ITexture[] getTexture(IGregTechTileEntity aBaseMetaTileEntity, ForgeDirection side, ForgeDirection facing, + int colorIndex, boolean aActive, boolean aRedstone) { + if (side == facing) { + if (aActive) return new ITexture[] { TextureFactory.of(MACHINE_CASING_MAGIC), TextureFactory.builder() + .addIcon(OVERLAY_TELEPORTER_ACTIVE) + .extFacing() + .build(), + TextureFactory.builder() + .addIcon(OVERLAY_TELEPORTER_ACTIVE_GLOW) + .extFacing() + .glow() + .build() }; + return new ITexture[] { TextureFactory.of(MACHINE_CASING_MAGIC), TextureFactory.builder() + .addIcon(OVERLAY_TELEPORTER) + .extFacing() + .build(), + TextureFactory.builder() + .addIcon(OVERLAY_TELEPORTER_GLOW) + .extFacing() + .glow() + .build() }; + } + if (aActive) return new ITexture[] { TextureFactory.of(MACHINE_CASING_MAGIC), TextureFactory.builder() + .addIcon(MACHINE_CASING_MAGIC_ACTIVE) + .extFacing() + .build(), + TextureFactory.builder() + .addIcon(MACHINE_CASING_MAGIC_ACTIVE_GLOW) + .extFacing() + .glow() + .build() }; + return new ITexture[] { TextureFactory.of(MACHINE_CASING_MAGIC), TextureFactory.builder() + .addIcon(MACHINE_CASING_MAGIC) + .extFacing() + .build(), + TextureFactory.builder() + .addIcon(MACHINE_CASING_MAGIC_GLOW) + .extFacing() + .glow() + .build() }; + } + + @Override + public boolean isCorrectMachinePart(ItemStack aStack) { + return true; + } + + @Override + public RecipeMap<?> getRecipeMap() { + return DEFCRecipes.fusionCraftingRecipes; + } + + @Override + protected ProcessingLogic createProcessingLogic() { + return new ProcessingLogic() { + + @NotNull + @Override + protected CheckRecipeResult validateRecipe(@NotNull GT_Recipe recipe) { + return recipe.mSpecialValue <= mTierCasing ? CheckRecipeResultRegistry.SUCCESSFUL + : CheckRecipeResultRegistry.insufficientMachineTier(recipe.mSpecialValue); + } + + @NotNull + @Override + protected GT_OverclockCalculator createOverclockCalculator(@NotNull GT_Recipe recipe) { + return super.createOverclockCalculator(recipe) + .setSpeedBoost(1f / (mTierCasing - recipe.mSpecialValue + 1)); + } + }; + } + + public int getMaxEfficiency(ItemStack aStack) { + return 10000; + } + + public int getPollutionPerTick(ItemStack aStack) { + return 0; + } + + public int getDamageToComponent(ItemStack aStack) { + return 0; + } + + public boolean explodesOnComponentBreak(ItemStack aStack) { + return false; + } + + @Override + public void construct(ItemStack itemStack, boolean b) { + buildPiece(STRUCTURE_PIECE_MAIN, itemStack, b, 2, 9, 0); + } + + @Override + public int survivalConstruct(ItemStack stackSize, int elementBudget, ISurvivalBuildEnvironment env) { + return survivialBuildPiece(STRUCTURE_PIECE_MAIN, stackSize, 2, 9, 0, elementBudget, env, true, true); + } + + @Override + public boolean supportsVoidProtection() { + return true; + } + + @Override + public boolean supportsInputSeparation() { + return true; + } + + @Override + public boolean supportsBatchMode() { + return true; + } +} diff --git a/src/main/java/kubatech/tileentity/gregtech/multiblock/GT_MetaTileEntity_ExtremeEntityCrusher.java b/src/main/java/kubatech/tileentity/gregtech/multiblock/GT_MetaTileEntity_ExtremeEntityCrusher.java new file mode 100644 index 0000000000..be0c628066 --- /dev/null +++ b/src/main/java/kubatech/tileentity/gregtech/multiblock/GT_MetaTileEntity_ExtremeEntityCrusher.java @@ -0,0 +1,822 @@ +/* + * spotless:off + * KubaTech - Gregtech Addon + * Copyright (C) 2022 - 2024 kuba6000 + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this library. If not, see <https://www.gnu.org/licenses/>. + * spotless:on + */ + +package kubatech.tileentity.gregtech.multiblock; + +import static com.gtnewhorizon.structurelib.structure.StructureUtility.isAir; +import static com.gtnewhorizon.structurelib.structure.StructureUtility.ofBlock; +import static com.gtnewhorizon.structurelib.structure.StructureUtility.onElementPass; +import static com.gtnewhorizon.structurelib.structure.StructureUtility.transpose; +import static gregtech.api.enums.GT_HatchElement.Energy; +import static gregtech.api.enums.GT_HatchElement.InputBus; +import static gregtech.api.enums.GT_HatchElement.Maintenance; +import static gregtech.api.enums.GT_HatchElement.OutputBus; +import static gregtech.api.enums.GT_HatchElement.OutputHatch; +import static gregtech.api.enums.Textures.BlockIcons.OVERLAY_FRONT_DISTILLATION_TOWER; +import static gregtech.api.enums.Textures.BlockIcons.OVERLAY_FRONT_DISTILLATION_TOWER_ACTIVE; +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.metatileentity.BaseTileEntity.TOOLTIP_DELAY; +import static gregtech.api.util.GT_StructureUtility.buildHatchAdder; +import static gregtech.api.util.GT_StructureUtility.ofFrame; +import static kubatech.api.Variables.Author; +import static kubatech.api.Variables.StructureHologram; + +import java.nio.charset.StandardCharsets; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Random; +import java.util.UUID; + +import net.minecraft.block.Block; +import net.minecraft.client.Minecraft; +import net.minecraft.enchantment.Enchantment; +import net.minecraft.enchantment.EnchantmentHelper; +import net.minecraft.entity.EnumCreatureAttribute; +import net.minecraft.entity.SharedMonsterAttributes; +import net.minecraft.entity.ai.attributes.AttributeModifier; +import net.minecraft.entity.player.EntityPlayer; +import net.minecraft.entity.player.EntityPlayerMP; +import net.minecraft.init.Blocks; +import net.minecraft.item.Item; +import net.minecraft.item.ItemStack; +import net.minecraft.nbt.NBTTagCompound; +import net.minecraft.tileentity.TileEntity; +import net.minecraft.util.ChunkCoordinates; +import net.minecraft.util.EnumChatFormatting; +import net.minecraft.world.EnumDifficulty; +import net.minecraft.world.World; +import net.minecraft.world.WorldProviderHell; +import net.minecraft.world.WorldServer; +import net.minecraftforge.common.MinecraftForge; +import net.minecraftforge.common.util.FakePlayer; +import net.minecraftforge.common.util.ForgeDirection; +import net.minecraftforge.fluids.FluidRegistry; +import net.minecraftforge.fluids.FluidStack; + +import org.jetbrains.annotations.NotNull; + +import com.github.bartimaeusnek.bartworks.API.BorosilicateGlass; +import com.google.common.collect.Multimap; +import com.gtnewhorizon.structurelib.alignment.IAlignmentLimits; +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.Text; +import com.gtnewhorizons.modularui.api.drawable.UITexture; +import com.gtnewhorizons.modularui.api.forge.ItemStackHandler; +import com.gtnewhorizons.modularui.api.math.Color; +import com.gtnewhorizons.modularui.api.screen.UIBuildContext; +import com.gtnewhorizons.modularui.common.widget.CycleButtonWidget; +import com.gtnewhorizons.modularui.common.widget.DynamicPositionedRow; +import com.gtnewhorizons.modularui.common.widget.SlotWidget; +import com.kuba6000.mobsinfo.api.utils.FastRandom; +import com.mojang.authlib.GameProfile; + +import WayofTime.alchemicalWizardry.api.alchemy.energy.ReagentRegistry; +import WayofTime.alchemicalWizardry.api.event.RitualRunEvent; +import WayofTime.alchemicalWizardry.api.rituals.Rituals; +import WayofTime.alchemicalWizardry.api.soulNetwork.SoulNetworkHandler; +import WayofTime.alchemicalWizardry.api.tile.IBloodAltar; +import WayofTime.alchemicalWizardry.common.rituals.RitualEffectWellOfSuffering; +import WayofTime.alchemicalWizardry.common.tileEntity.TEMasterStone; +import cpw.mods.fml.common.eventhandler.EventPriority; +import cpw.mods.fml.common.eventhandler.SubscribeEvent; +import cpw.mods.fml.relauncher.Side; +import cpw.mods.fml.relauncher.SideOnly; +import crazypants.enderio.EnderIO; +import gregtech.api.GregTech_API; +import gregtech.api.enums.Materials; +import gregtech.api.enums.Textures; +import gregtech.api.gui.modularui.GT_UITextures; +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_InputBus; +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 kubatech.Tags; +import kubatech.api.LoaderReference; +import kubatech.api.helpers.ReflectionHelper; +import kubatech.api.implementations.KubaTechGTMultiBlockBase; +import kubatech.api.tileentity.CustomTileEntityPacketHandler; +import kubatech.api.utils.ModUtils; +import kubatech.client.effect.EntityRenderer; +import kubatech.loaders.MobHandlerLoader; +import kubatech.network.CustomTileEntityPacket; + +public class GT_MetaTileEntity_ExtremeEntityCrusher + extends KubaTechGTMultiBlockBase<GT_MetaTileEntity_ExtremeEntityCrusher> + implements CustomTileEntityPacketHandler, ISurvivalConstructable { + + public static final double DIAMOND_SPIKES_DAMAGE = 9d; + // Powered spawner with octadic capacitor spawns ~22/min ~= 0.366/sec ~= 2.72s/spawn ~= 54.54t/spawn + public static final int MOB_SPAWN_INTERVAL = 55; + public final Random rand = new FastRandom(); + private final WeaponCache weaponCache; + + @SuppressWarnings("unused") + public GT_MetaTileEntity_ExtremeEntityCrusher(int aID, String aName, String aNameRegional) { + super(aID, aName, aNameRegional); + weaponCache = new WeaponCache(mInventory); + } + + public GT_MetaTileEntity_ExtremeEntityCrusher(String aName) { + super(aName); + weaponCache = new WeaponCache(mInventory); + if (LoaderReference.BloodMagic) MinecraftForge.EVENT_BUS.register(this); + } + + @Override + public void onRemoval() { + if (LoaderReference.BloodMagic) MinecraftForge.EVENT_BUS.unregister(this); + if (getBaseMetaTileEntity().isClientSide() && entityRenderer != null) { + entityRenderer.setDead(); + } + } + + @Override + public void onUnload() { + if (LoaderReference.BloodMagic) MinecraftForge.EVENT_BUS.unregister(this); + } + + private static final String WellOfSufferingRitualName = "AW013Suffering"; + + private static final Item poweredSpawnerItem = Item.getItemFromBlock(EnderIO.blockPoweredSpawner); + private static final int CASING_INDEX = 16; + private static final String STRUCTURE_PIECE_MAIN = "main"; + private static final IStructureDefinition<GT_MetaTileEntity_ExtremeEntityCrusher> STRUCTURE_DEFINITION = StructureDefinition + .<GT_MetaTileEntity_ExtremeEntityCrusher>builder() + .addShape( + STRUCTURE_PIECE_MAIN, + transpose( + new String[][] { // spotless:off + { "ccccc", "ccccc", "ccccc", "ccccc", "ccccc" }, + { "fgggf", "g---g", "g---g", "g---g", "fgggf" }, + { "fgggf", "g---g", "g---g", "g---g", "fgggf" }, + { "fgggf", "g---g", "g---g", "g---g", "fgggf" }, + { "fgggf", "g---g", "g---g", "g---g", "fgggf" }, + { "fgggf", "gsssg", "gsssg", "gsssg", "fgggf" }, + { "CC~CC", "CCCCC", "CCCCC", "CCCCC", "CCCCC" }, + })) // spotless:on + .addElement('c', onElementPass(t -> t.mCasing++, ofBlock(GregTech_API.sBlockCasings2, 0))) + .addElement( + 'C', + buildHatchAdder(GT_MetaTileEntity_ExtremeEntityCrusher.class) + .atLeast(InputBus, OutputBus, OutputHatch, Energy, Maintenance) + .casingIndex(CASING_INDEX) + .dot(1) + .buildAndChain(onElementPass(t -> t.mCasing++, ofBlock(GregTech_API.sBlockCasings2, 0)))) + .addElement( + 'g', + LoaderReference.Bartworks + ? BorosilicateGlass.ofBoroGlass((byte) 0, (t, v) -> t.mGlassTier = v, t -> t.mGlassTier) + : onElementPass(t -> t.mGlassTier = 100, ofBlock(Blocks.glass, 0))) + .addElement('f', ofFrame(Materials.Steel)) + .addElement( + 's', + LoaderReference.ExtraUtilities ? ofBlock(Block.getBlockFromName("ExtraUtilities:spike_base_diamond"), 0) + : isAir()) + .build(); + + private TileEntity masterStoneRitual = null; + private TileEntity tileAltar = null; + private boolean isInRitualMode = false; + private int mCasing = 0; + private byte mGlassTier = 0; + private boolean mAnimationEnabled = true; + private boolean mIsProducingInfernalDrops = true; + private boolean voidAllDamagedAndEnchantedItems = false; + + private EntityRenderer entityRenderer = null; + private boolean renderEntity = false; + public EECFakePlayer EECPlayer = null; + + @Override + public void saveNBTData(NBTTagCompound aNBT) { + super.saveNBTData(aNBT); + aNBT.setBoolean("isInRitualMode", isInRitualMode); + aNBT.setBoolean("mAnimationEnabled", mAnimationEnabled); + aNBT.setByte("mGlassTier", mGlassTier); + aNBT.setBoolean("mIsProducingInfernalDrops", mIsProducingInfernalDrops); + aNBT.setBoolean("voidAllDamagedAndEnchantedItems", voidAllDamagedAndEnchantedItems); + if (weaponCache.getStackInSlot(0) != null) aNBT.setTag( + "weaponCache", + weaponCache.getStackInSlot(0) + .writeToNBT(new NBTTagCompound())); + } + + @Override + public void loadNBTData(NBTTagCompound aNBT) { + super.loadNBTData(aNBT); + isInRitualMode = aNBT.getBoolean("isInRitualMode"); + mAnimationEnabled = !aNBT.hasKey("mAnimationEnabled") || aNBT.getBoolean("mAnimationEnabled"); + mGlassTier = aNBT.getByte("mGlassTier"); + mIsProducingInfernalDrops = !aNBT.hasKey("mIsProducingInfernalDrops") + || aNBT.getBoolean("mIsProducingInfernalDrops"); + voidAllDamagedAndEnchantedItems = aNBT.getBoolean("voidAllDamagedAndEnchantedItems"); + if (aNBT.hasKey("weaponCache")) + weaponCache.setStackInSlot(0, ItemStack.loadItemStackFromNBT(aNBT.getCompoundTag("weaponCache"))); + } + + @Override + public boolean isOverclockingInfinite() { + return true; + } + + @Override + protected int getOverclockTimeLimit() { + return 20; + } + + @Override + public IStructureDefinition<GT_MetaTileEntity_ExtremeEntityCrusher> getStructureDefinition() { + return STRUCTURE_DEFINITION; + } + + @Override + protected IAlignmentLimits getInitialAlignmentLimits() { + return (d, r, f) -> d.offsetY == 0 && r.isNotRotated(); + } + + @Override + protected GT_Multiblock_Tooltip_Builder createTooltip() { + GT_Multiblock_Tooltip_Builder tt = new GT_Multiblock_Tooltip_Builder(); + tt.addMachineType("Powered Spawner, EEC") + .addInfo("Controller block for the Extreme Entity Crusher") + .addInfo(Author) + .addInfo("Spawns and kills monsters for you.") + .addInfo("You have to insert the powered spawner in the controller.") + .addInfo("Base energy usage: 2,000 EU/t") + .addInfo("Supports perfect OC, minimum time: 20 ticks, after that multiplies the outputs.") + .addInfo("Recipe time is based on mob health.") + .addInfo("You can additionally put a weapon inside the GUI.") + .addInfo("It will speed up the process and apply the looting level from the weapon (maximum 4 levels).") + .addInfo(EnumChatFormatting.RED + "Enchanting the spikes inside does nothing!") + .addInfo("Also produces 120 Liquid XP per operation.") + .addInfo("If the mob spawns infernal, it will drain 8 times more power.") + .addInfo("You can prevent infernal spawns by shift clicking with a screwdriver.") + .addInfo("Note: If the mob has forced infernal spawn, it will do it anyway.") + .addInfo("You can enable ritual mode with a screwdriver.") + .addInfo("When in ritual mode and the Well Of Suffering ritual is built directly centered on the machine,") + .addInfo("the mobs will start to buffer and die very slowly by the ritual.") + .addInfo("You can disable mob animation with a soldering iron.") + .addInfo(StructureHologram) + .addSeparator() + .beginStructureBlock(5, 7, 5, true) + .addController("Front Bottom Center") + .addCasingInfoMin("Solid Steel Machine Casing", 35, false) + .addOtherStructurePart("Tiered (HV+) Glass", "Side walls without edges or corners") + .addStructureInfo("The glass tier limits the Energy Hatch tier") + .addOtherStructurePart("Steel Frame Box", "All vertical edges without corners") + .addOtherStructurePart("Diamond spikes", "Inside second layer") + .addOutputBus("Any bottom casing", 1) + .addOutputHatch("Any bottom casing", 1) + .addEnergyHatch("Any bottom casing", 1) + .addMaintenanceHatch("Any bottom casing", 1) + .toolTipFinisher(Tags.MODNAME); + return tt; + } + + @Override + public void construct(ItemStack itemStack, boolean b) { + buildPiece(STRUCTURE_PIECE_MAIN, itemStack, b, 2, 6, 0); + } + + @Override + public int survivalConstruct(ItemStack stackSize, int elementBudget, ISurvivalBuildEnvironment env) { + return survivialBuildPiece(STRUCTURE_PIECE_MAIN, stackSize, 2, 6, 0, elementBudget, env, true, true); + } + + @Override + public IMetaTileEntity newMetaEntity(IGregTechTileEntity aTileEntity) { + return new GT_MetaTileEntity_ExtremeEntityCrusher(this.mName); + } + + @Override + public ITexture[] getTexture(IGregTechTileEntity aBaseMetaTileEntity, ForgeDirection side, ForgeDirection facing, + int colorIndex, boolean aActive, boolean aRedstone) { + if (side == facing) { + if (aActive) return new ITexture[] { Textures.BlockIcons.getCasingTextureForId(CASING_INDEX), + TextureFactory.builder() + .addIcon(OVERLAY_FRONT_DISTILLATION_TOWER_ACTIVE) + .extFacing() + .build(), + TextureFactory.builder() + .addIcon(OVERLAY_FRONT_DISTILLATION_TOWER_ACTIVE_GLOW) + .extFacing() + .glow() + .build() }; + return new ITexture[] { Textures.BlockIcons.getCasingTextureForId(CASING_INDEX), TextureFactory.builder() + .addIcon(OVERLAY_FRONT_DISTILLATION_TOWER) + .extFacing() + .build(), + TextureFactory.builder() + .addIcon(OVERLAY_FRONT_DISTILLATION_TOWER_GLOW) + .extFacing() + .glow() + .build() }; + } + return new ITexture[] { Textures.BlockIcons.getCasingTextureForId(CASING_INDEX) }; + } + + @SideOnly(Side.CLIENT) + private void setupEntityRenderer(IGregTechTileEntity aBaseMetaTileEntity, int time) { + if (entityRenderer == null) { + ChunkCoordinates coords = this.getBaseMetaTileEntity() + .getCoords(); + int[] abc = new int[] { 0, -2, 2 }; + int[] xyz = new int[] { 0, 0, 0 }; + this.getExtendedFacing() + .getWorldOffset(abc, xyz); + xyz[0] += coords.posX; + xyz[1] += coords.posY; + xyz[2] += coords.posZ; + entityRenderer = new EntityRenderer(aBaseMetaTileEntity.getWorld(), xyz[0], xyz[1], xyz[2], time); + } else { + entityRenderer.setDead(); + entityRenderer = new EntityRenderer(entityRenderer, time); + } + Minecraft.getMinecraft().effectRenderer.addEffect(entityRenderer); + } + + @Override + public void onPostTick(IGregTechTileEntity aBaseMetaTileEntity, long aTick) { + super.onPostTick(aBaseMetaTileEntity, aTick); + if (aBaseMetaTileEntity.isClientSide()) { + if (renderEntity && aBaseMetaTileEntity.isActive() && aTick % 40 == 0) { + setupEntityRenderer(aBaseMetaTileEntity, 40); + } + } + } + + @SideOnly(Side.CLIENT) + @Override + public void HandleCustomPacket(CustomTileEntityPacket message) { + if (message.getDataBoolean()) { + renderEntity = true; + String mobType = message.getDataString(); + MobHandlerLoader.MobEECRecipe r = MobHandlerLoader.recipeMap.get(mobType); + if (r != null) { + if (entityRenderer == null) setupEntityRenderer(getBaseMetaTileEntity(), 40); + entityRenderer.setEntity(r.entityCopy); + } else entityRenderer.setEntity(null); + } else { + renderEntity = false; + if (entityRenderer != null) { + entityRenderer.setDead(); + entityRenderer = null; + } + } + } + + @Override + public void onScrewdriverRightClick(ForgeDirection side, EntityPlayer aPlayer, float aX, float aY, float aZ) { + if (this.mMaxProgresstime > 0) { + GT_Utility.sendChatToPlayer(aPlayer, "Can't change mode when running !"); + return; + } + if (aPlayer.isSneaking()) { + if (!LoaderReference.InfernalMobs) return; + mIsProducingInfernalDrops = !mIsProducingInfernalDrops; + if (!mIsProducingInfernalDrops) + GT_Utility.sendChatToPlayer(aPlayer, "Mobs will now be prevented from spawning infernal"); + else GT_Utility.sendChatToPlayer(aPlayer, "Mobs can spawn infernal now"); + } else { + if (!LoaderReference.BloodMagic) return; + isInRitualMode = !isInRitualMode; + if (!isInRitualMode) { + GT_Utility.sendChatToPlayer(aPlayer, "Ritual mode disabled"); + } else { + GT_Utility.sendChatToPlayer(aPlayer, "Ritual mode enabled"); + if (connectToRitual()) GT_Utility.sendChatToPlayer(aPlayer, "Successfully connected to the ritual"); + else GT_Utility.sendChatToPlayer(aPlayer, "Can't connect to the ritual"); + } + } + } + + @Override + public boolean onSolderingToolRightClick(ForgeDirection side, ForgeDirection wrenchingSide, EntityPlayer aPlayer, + float aX, float aY, float aZ) { + if (wrenchingSide == getBaseMetaTileEntity().getFrontFacing()) { + mAnimationEnabled = !mAnimationEnabled; + GT_Utility.sendChatToPlayer(aPlayer, "Animations are " + (mAnimationEnabled ? "enabled" : "disabled")); + return true; + } else return super.onSolderingToolRightClick(side, wrenchingSide, aPlayer, aX, aY, aZ); + } + + @SuppressWarnings("unused") + @SubscribeEvent(priority = EventPriority.LOWEST) + public void onRitualPerform(RitualRunEvent event) { + if (!isInRitualMode) return; + if (masterStoneRitual == null) return; + if (this.mMaxProgresstime == 0) return; + if (event.mrs.equals(masterStoneRitual) && event.ritualKey.equals(WellOfSufferingRitualName)) { + Rituals ritual = Rituals.ritualMap.get(WellOfSufferingRitualName); + if (ritual != null && ritual.effect instanceof RitualEffectWellOfSuffering) { + RitualEffectWellOfSuffering effect = (RitualEffectWellOfSuffering) ritual.effect; + event.setCanceled(true); // we will handle that + String owner = event.mrs.getOwner(); + int currentEssence = SoulNetworkHandler.getCurrentEssence(owner); + World world = event.mrs.getWorld(); + int x = event.mrs.getXCoord(); + int y = event.mrs.getYCoord(); + int z = event.mrs.getZCoord(); + + if (world.getWorldTime() % RitualEffectWellOfSuffering.timeDelay != 0) return; + + if (tileAltar == null || tileAltar.isInvalid()) { + tileAltar = null; + for (int i = -5; i <= 5; i++) for (int j = -5; j <= 5; j++) for (int k = -10; k <= 10; k++) + if (world.getTileEntity(x + i, y + k, z + j) instanceof IBloodAltar) + tileAltar = world.getTileEntity(x + i, y + k, z + j); + } + if (tileAltar == null) return; + + if (currentEssence < effect.getCostPerRefresh() * 100) { + SoulNetworkHandler.causeNauseaToPlayer(owner); + return; + } + + ((IBloodAltar) tileAltar).sacrificialDaggerCall( + 100 * RitualEffectWellOfSuffering.amount + * (effect.canDrainReagent( + event.mrs, + ReagentRegistry.offensaReagent, + ReflectionHelper.getField(effect, "offensaDrain", 3), + true) ? 2 : 1) + * (effect.canDrainReagent( + event.mrs, + ReagentRegistry.tenebraeReagent, + ReflectionHelper.getField(effect, "tennebraeDrain", 5), + true) ? 2 : 1), + true); + + SoulNetworkHandler.syphonFromNetwork(owner, effect.getCostPerRefresh() * 100); + } + } + } + + private CustomTileEntityPacket mobPacket = null; + + private static class WeaponCache extends ItemStackHandler { + + boolean isValid = false; + int looting = 0; + double attackDamage = 0; + + public WeaponCache(ItemStack[] inventory) { + super(inventory); + } + + @Override + protected void onContentsChanged(int slot) { + if (slot != 0) return; + if (ModUtils.isClientThreaded()) return; + ItemStack stack = getStackInSlot(0); + if (stack == null) { + isValid = false; + return; + } + // noinspection unchecked + attackDamage = ((Multimap<String, AttributeModifier>) stack.getAttributeModifiers()) + .get(SharedMonsterAttributes.attackDamage.getAttributeUnlocalizedName()) + .stream() + .mapToDouble( + attr -> attr.getAmount() + + (double) EnchantmentHelper.func_152377_a(stack, EnumCreatureAttribute.UNDEFINED)) + .sum(); + looting = Math.min(4, EnchantmentHelper.getEnchantmentLevel(Enchantment.looting.effectId, stack)); + isValid = true; + } + + @Override + public boolean isItemValid(int slot, ItemStack stack) { + return Enchantment.looting.canApply(stack); + } + } + + @Override + public boolean isValidSlot(int aIndex) { + return aIndex >= 0; + } + + @SuppressWarnings("unlikely-arg-type") + @Override + @NotNull + public CheckRecipeResult checkProcessing() { + if (getBaseMetaTileEntity().isClientSide()) return CheckRecipeResultRegistry.NO_RECIPE; + ItemStack aStack = mInventory[1]; + if (aStack == null) return SimpleCheckRecipeResult.ofFailure("EEC_nospawner"); + + if (aStack.getItem() != poweredSpawnerItem) return SimpleCheckRecipeResult.ofFailure("EEC_nospawner"); + + if (aStack.getTagCompound() == null) return SimpleCheckRecipeResult.ofFailure("EEC_invalidspawner"); + String mobType = aStack.getTagCompound() + .getString("mobType"); + if (mobType.isEmpty()) return SimpleCheckRecipeResult.ofFailure("EEC_invalidspawner"); + + if (mobType.equals("Skeleton") && getBaseMetaTileEntity().getWorld().provider instanceof WorldProviderHell + && rand.nextInt(5) > 0) mobType = "witherSkeleton"; + + MobHandlerLoader.MobEECRecipe recipe = MobHandlerLoader.recipeMap.get(mobType); + + if (recipe == null) return CheckRecipeResultRegistry.NO_RECIPE; + if (!recipe.recipe.isPeacefulAllowed && this.getBaseMetaTileEntity() + .getWorld().difficultySetting == EnumDifficulty.PEACEFUL) + return SimpleCheckRecipeResult.ofFailure("EEC_peaceful"); + + if (isInRitualMode && isRitualValid()) { + if (getMaxInputEu() < recipe.mEUt / 4) return CheckRecipeResultRegistry.insufficientPower(recipe.mEUt / 4); + this.mOutputFluids = new FluidStack[] { FluidRegistry.getFluidStack("xpjuice", 5000) }; + this.mOutputItems = recipe + .generateOutputs(rand, this, 3, 0, mIsProducingInfernalDrops, voidAllDamagedAndEnchantedItems); + this.lEUt /= 4L; + this.mMaxProgresstime = 400; + } else { + if (getMaxInputEu() < recipe.mEUt) return CheckRecipeResultRegistry.insufficientPower(recipe.mEUt); + if (recipe.recipe.alwaysinfernal && getMaxInputEu() < recipe.mEUt * 8) + return CheckRecipeResultRegistry.insufficientPower(recipe.mEUt * 8); + + double attackDamage = DIAMOND_SPIKES_DAMAGE; // damage from spikes + weaponCheck: { + GT_MetaTileEntity_Hatch_InputBus inputbus = this.mInputBusses.size() == 0 ? null + : this.mInputBusses.get(0); + if (inputbus != null && !inputbus.isValid()) inputbus = null; + ItemStack lootingHolder = inputbus == null ? null : inputbus.getStackInSlot(0); + if (lootingHolder == null) break weaponCheck; + if (weaponCache.getStackInSlot(0) != null) break weaponCheck; + if (weaponCache.isItemValid(0, lootingHolder)) { + weaponCache.setStackInSlot(0, lootingHolder); + inputbus.setInventorySlotContents(0, null); + updateSlots(); + } + } + if (weaponCache.isValid) attackDamage += weaponCache.attackDamage; + + if (EECPlayer == null) EECPlayer = new EECFakePlayer(this); + EECPlayer.currentWeapon = weaponCache.getStackInSlot(0); + + this.mOutputItems = recipe.generateOutputs( + rand, + this, + attackDamage, + weaponCache.isValid ? weaponCache.looting : 0, + mIsProducingInfernalDrops, + voidAllDamagedAndEnchantedItems); + + EECPlayer.currentWeapon = null; + + this.mOutputFluids = new FluidStack[] { FluidRegistry.getFluidStack("xpjuice", 120) }; + ItemStack weapon = weaponCache.getStackInSlot(0); + int times = this.calculatePerfectOverclock(this.lEUt, this.mMaxProgresstime); + if (weaponCache.isValid && weapon.isItemStackDamageable()) { + EECPlayer.currentWeapon = weapon; + Item lootingHolderItem = weapon.getItem(); + for (int i = 0; i < times + 1; i++) { + // noinspection ConstantConditions + if (!lootingHolderItem.hitEntity(weapon, recipe.recipe.entity, EECPlayer)) break; + if (weapon.stackSize == 0) { + weaponCache.setStackInSlot(0, null); + break; + } + } + EECPlayer.currentWeapon = null; + } + } + if (this.lEUt > 0) this.lEUt = -this.lEUt; + this.mEfficiency = (10000 - (getIdealStatus() - getRepairStatus()) * 1000); + this.mEfficiencyIncrease = 10000; + + if (mobPacket == null) mobPacket = new CustomTileEntityPacket((TileEntity) this.getBaseMetaTileEntity(), null); + mobPacket.resetHelperData(); + mobPacket.addData(mAnimationEnabled); + if (mAnimationEnabled) mobPacket.addData(mobType); + mobPacket.sendToAllAround(16); + + this.updateSlots(); + return CheckRecipeResultRegistry.SUCCESSFUL; + } + + private boolean isRitualValid() { + if (!isInRitualMode) return false; + if (masterStoneRitual == null) return false; + if (masterStoneRitual.isInvalid() || !(((TEMasterStone) masterStoneRitual).getCurrentRitual() + .equals(WellOfSufferingRitualName))) { + masterStoneRitual = null; + return false; + } + return true; + } + + private boolean connectToRitual() { + if (!LoaderReference.BloodMagic) return false; + ChunkCoordinates coords = this.getBaseMetaTileEntity() + .getCoords(); + int[] abc = new int[] { 0, -8, 2 }; + int[] xyz = new int[] { 0, 0, 0 }; + this.getExtendedFacing() + .getWorldOffset(abc, xyz); + xyz[0] += coords.posX; + xyz[1] += coords.posY; + xyz[2] += coords.posZ; + TileEntity te = this.getBaseMetaTileEntity() + .getTileEntity(xyz[0], xyz[1], xyz[2]); + if (te instanceof TEMasterStone) { + if (((TEMasterStone) te).getCurrentRitual() + .equals(WellOfSufferingRitualName)) { + masterStoneRitual = te; + return true; + } + } + return false; + } + + @Override + public boolean checkMachine(IGregTechTileEntity aBaseMetaTileEntity, ItemStack aStack) { + mGlassTier = 0; + mCasing = 0; + if (!checkPiece(STRUCTURE_PIECE_MAIN, 2, 6, 0)) return false; + if (mCasing < 35 || mMaintenanceHatches.size() != 1 + || mEnergyHatches.size() == 0 + || !(mInputBusses.size() == 0 || (mInputBusses.size() == 1 && mInputBusses.get(0).mTier == 0))) + return false; + if (mGlassTier < 8) + for (GT_MetaTileEntity_Hatch_Energy hatch : mEnergyHatches) if (hatch.mTier > mGlassTier) return false; + if (isInRitualMode) connectToRitual(); + return true; + } + + @Override + public String[] getInfoData() { + ArrayList<String> info = new ArrayList<>(Arrays.asList(super.getInfoData())); + info.add("Animations: " + EnumChatFormatting.YELLOW + (mAnimationEnabled ? "Enabled" : "Disabled")); + info.add( + "Is allowed to produce infernal drops: " + EnumChatFormatting.YELLOW + + (mIsProducingInfernalDrops ? "Yes" : "No")); + info.add( + "Void all damaged and enchanted items: " + EnumChatFormatting.YELLOW + + (voidAllDamagedAndEnchantedItems ? "Yes" : "No")); + info.add("Is in ritual mode: " + EnumChatFormatting.YELLOW + (isInRitualMode ? "Yes" : "No")); + if (isInRitualMode) info.add( + "Is connected to ritual: " + + (isRitualValid() ? EnumChatFormatting.GREEN + "Yes" : EnumChatFormatting.RED + "No")); + else { + info.add("Inserted weapon: " + EnumChatFormatting.YELLOW + (weaponCache.isValid ? "Yes" : "No")); + if (weaponCache.isValid) { + info.add("Weapon attack damage: " + EnumChatFormatting.YELLOW + weaponCache.attackDamage); + info.add("Weapon looting level: " + EnumChatFormatting.YELLOW + weaponCache.looting); + info.add( + "Total attack damage: " + EnumChatFormatting.YELLOW + + (DIAMOND_SPIKES_DAMAGE + weaponCache.attackDamage)); + } else info.add("Total attack damage: " + EnumChatFormatting.YELLOW + DIAMOND_SPIKES_DAMAGE); + } + return info.toArray(new String[0]); + } + + @Override + protected void addConfigurationWidgets(DynamicPositionedRow configurationElements, UIBuildContext buildContext) { + configurationElements.setSynced(true); + configurationElements.widget(new CycleButtonWidget().setToggle(() -> isInRitualMode, v -> { + if (this.mMaxProgresstime > 0) { + GT_Utility.sendChatToPlayer(buildContext.getPlayer(), "Can't change mode when running !"); + return; + } + + isInRitualMode = v; + + if (!(buildContext.getPlayer() instanceof EntityPlayerMP)) return; + if (!isInRitualMode) { + GT_Utility.sendChatToPlayer(buildContext.getPlayer(), "Ritual mode disabled"); + } else { + GT_Utility.sendChatToPlayer(buildContext.getPlayer(), "Ritual mode enabled"); + if (connectToRitual()) + GT_Utility.sendChatToPlayer(buildContext.getPlayer(), "Successfully connected to the ritual"); + else GT_Utility.sendChatToPlayer(buildContext.getPlayer(), "Can't connect to the ritual"); + } + }) + .setTextureGetter(toggleButtonTextureGetter) + .setVariableBackgroundGetter(toggleButtonBackgroundGetter) + .setSize(16, 16) + .addTooltip("Ritual mode") + .setTooltipShowUpDelay(TOOLTIP_DELAY)); + configurationElements.widget(new CycleButtonWidget().setToggle(() -> mIsProducingInfernalDrops, v -> { + if (this.mMaxProgresstime > 0) { + GT_Utility.sendChatToPlayer(buildContext.getPlayer(), "Can't change mode when running !"); + return; + } + + mIsProducingInfernalDrops = v; + + if (!(buildContext.getPlayer() instanceof EntityPlayerMP)) return; + if (!mIsProducingInfernalDrops) GT_Utility + .sendChatToPlayer(buildContext.getPlayer(), "Mobs will now be prevented from spawning infernal"); + else GT_Utility.sendChatToPlayer(buildContext.getPlayer(), "Mobs can spawn infernal now"); + }) + .setTextureGetter(toggleButtonTextureGetter) + .setVariableBackgroundGetter(toggleButtonBackgroundGetter) + .setSize(16, 16) + .addTooltip("Is allowed to spawn infernal mobs") + .addTooltip(new Text("Does not affect mobs that are always infernal !").color(Color.GRAY.normal)) + .setTooltipShowUpDelay(TOOLTIP_DELAY)); + configurationElements.widget(new CycleButtonWidget().setToggle(() -> voidAllDamagedAndEnchantedItems, v -> { + if (this.mMaxProgresstime > 0) { + GT_Utility.sendChatToPlayer(buildContext.getPlayer(), "Can't change mode when running !"); + return; + } + + voidAllDamagedAndEnchantedItems = v; + + if (!(buildContext.getPlayer() instanceof EntityPlayerMP)) return; + if (!voidAllDamagedAndEnchantedItems) GT_Utility.sendChatToPlayer(buildContext.getPlayer(), "Void nothing"); + else GT_Utility.sendChatToPlayer(buildContext.getPlayer(), "Void all damaged and enchanted items"); + }) + .setTextureGetter(toggleButtonTextureGetter) + .setVariableBackgroundGetter(toggleButtonBackgroundGetter) + .setSize(16, 16) + .addTooltip("Void all damaged and enchanted items") + .addTooltip( + new Text("Does not affect infernal drops and some special drops like Sticky Sword!") + .color(Color.GRAY.normal)) + .setTooltipShowUpDelay(TOOLTIP_DELAY)); + } + + @Override + public void createInventorySlots() { + final SlotWidget spawnerSlot = new SlotWidget(inventoryHandler, 1); + spawnerSlot.setBackground( + GT_UITextures.SLOT_DARK_GRAY, + UITexture.fullImage(Tags.MODID, "gui/slot/gray_spawner") + .withFixedSize(16, 16) + .withOffset(1, 1)); + spawnerSlot.setFilter(stack -> stack.getItem() == poweredSpawnerItem); + slotWidgets.add(spawnerSlot); + final SlotWidget weaponSlot = new SlotWidget(weaponCache, 0); + weaponSlot.setBackground( + GT_UITextures.SLOT_DARK_GRAY, + UITexture.fullImage(Tags.MODID, "gui/slot/gray_sword") + .withFixedSize(16, 16) + .withOffset(1, 1)); + slotWidgets.add(weaponSlot); + } + + private static class EECFakePlayer extends FakePlayer { + + GT_MetaTileEntity_ExtremeEntityCrusher mte; + ItemStack currentWeapon; + + public EECFakePlayer(GT_MetaTileEntity_ExtremeEntityCrusher mte) { + super( + (WorldServer) mte.getBaseMetaTileEntity() + .getWorld(), + new GameProfile( + UUID.nameUUIDFromBytes("[EEC Fake Player]".getBytes(StandardCharsets.UTF_8)), + "[EEC Fake Player]")); + this.mte = mte; + } + + @Override + public void renderBrokenItemStack(ItemStack p_70669_1_) {} + + @Override + public Random getRNG() { + return mte.rand; + } + + @Override + public void destroyCurrentEquippedItem() {} + + @Override + public ItemStack getCurrentEquippedItem() { + return currentWeapon; + } + + @Override + public ItemStack getHeldItem() { + return currentWeapon; + } + } +} 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 new file mode 100644 index 0000000000..4548e1adca --- /dev/null +++ b/src/main/java/kubatech/tileentity/gregtech/multiblock/GT_MetaTileEntity_ExtremeIndustrialGreenhouse.java @@ -0,0 +1,1547 @@ +/* + * spotless:off + * KubaTech - Gregtech Addon + * Copyright (C) 2022 - 2024 kuba6000 + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this library. If not, see <https://www.gnu.org/licenses/>. + * spotless:on + */ + +package kubatech.tileentity.gregtech.multiblock; + +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.Textures.BlockIcons.OVERLAY_FRONT_DISTILLATION_TOWER; +import static gregtech.api.enums.Textures.BlockIcons.OVERLAY_FRONT_DISTILLATION_TOWER_ACTIVE; +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 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.HashMap; +import java.util.Iterator; +import java.util.List; +import java.util.Map; +import java.util.Objects; +import java.util.Random; + +import net.minecraft.block.Block; +import net.minecraft.block.BlockFlower; +import net.minecraft.block.BlockStem; +import net.minecraft.client.Minecraft; +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.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; +import net.minecraftforge.fluids.FluidStack; + +import org.jetbrains.annotations.NotNull; + +import com.github.bartimaeusnek.bartworks.API.BorosilicateGlass; +import com.gtnewhorizon.structurelib.alignment.IAlignmentLimits; +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; +import com.gtnewhorizons.modularui.api.screen.ModularWindow; +import com.gtnewhorizons.modularui.api.screen.UIBuildContext; +import com.gtnewhorizons.modularui.common.builder.UIInfo; +import com.gtnewhorizons.modularui.common.internal.wrapper.ModularUIContainer; +import com.gtnewhorizons.modularui.common.widget.ButtonWidget; +import com.gtnewhorizons.modularui.common.widget.Column; +import com.gtnewhorizons.modularui.common.widget.CycleButtonWidget; +import com.gtnewhorizons.modularui.common.widget.DrawableWidget; +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.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.GT_Values; +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.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.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 ic2.core.init.BlocksItems; +import ic2.core.init.InternalName; +import kubatech.Tags; +import kubatech.api.DynamicInventory; +import kubatech.api.LoaderReference; +import kubatech.api.implementations.KubaTechGTMultiBlockBase; +import kubatech.client.effect.CropRenderer; + +@SuppressWarnings("unused") +public class GT_MetaTileEntity_ExtremeIndustrialGreenhouse + extends KubaTechGTMultiBlockBase<GT_MetaTileEntity_ExtremeIndustrialGreenhouse> { + + /*** + * BALANCE OF THE IC2 MODE: + * (let T = EIG_BALANCE_IC2_ACCELERATOR_TIER) + * All IC2 crops are simulated and all drops are generated based on the real crop drops. + * T is a tick accelerator tier for the IC2 crops, + * Each crop in the EIG is accelerated using T tier accelerator + * (Accelerators in the game are defined as 2^T acceleration, 8*(4^T) voltage, 6 amps) + * IC2 mode is unlocked at T+1 tier (glass and power) + * And each amp of T gives one crop slot, EIG only consumes 1 AMP of a tier that it is at + * (EIG starts at 4 crops (T+1 tier) and each tier quadruples the amount of slots) + * Each crop is accelerated 2^T times + * Summary: + * Accelerators in EIG are a bit cheaper than on the crop field (4 amps instead of 6 amps) + * There are 4 crops touching the accelerator (1 AMP for 1 accelerated crop) + * + * 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; + + 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 CONFIGURATION_WINDOW_ID = 999; + + public final List<GreenHouseSlot> mStorage = new ArrayList<>(); + private int oldVersion = 0; + 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<GT_MetaTileEntity_ExtremeIndustrialGreenhouse> STRUCTURE_DEFINITION = StructureDefinition + .<GT_MetaTileEntity_ExtremeIndustrialGreenhouse>builder() + .addShape( + STRUCTURE_PIECE_MAIN, + transpose( + new String[][] { // spotless:off + { "ccccc", "ccccc", "ccccc", "ccccc", "ccccc" }, + { "ccccc", "clllc", "clllc", "clllc", "ccccc" }, + { "ggggg", "g---g", "g---g", "g---g", "ggggg" }, + { "ggggg", "g---g", "g---g", "g---g", "ggggg" }, + { "ccccc", "cdddc", "cdwdc", "cdddc", "ccccc" }, + { "cc~cc", "cCCCc", "cCCCc", "cCCCc", "ccccc" }, + })) // spotless:on + .addElement( + 'c', + ofChain( + onElementPass(t -> t.mCasing++, ofBlock(GregTech_API.sBlockCasings4, 1)), + ofHatchAdder( + GT_MetaTileEntity_ExtremeIndustrialGreenhouse::addEnergyInputToMachineList, + CASING_INDEX, + 1), + ofHatchAdder( + GT_MetaTileEntity_ExtremeIndustrialGreenhouse::addMaintenanceToMachineList, + CASING_INDEX, + 1), + ofHatchAdder(GT_MetaTileEntity_ExtremeIndustrialGreenhouse::addInputToMachineList, CASING_INDEX, 1), + ofHatchAdder(GT_MetaTileEntity_ExtremeIndustrialGreenhouse::addOutputToMachineList, CASING_INDEX, 1))) + .addElement('C', onElementPass(t -> t.mCasing++, ofBlock(GregTech_API.sBlockCasings4, 1))) + .addElement( + 'l', + LoaderReference.ProjRedIllumination + ? ofBlock(Block.getBlockFromName("ProjRed|Illumination:projectred.illumination.lamp"), 10) + : ofChain(ofBlock(Blocks.redstone_lamp, 0), ofBlock(Blocks.lit_redstone_lamp, 0))) + .addElement( + 'g', + LoaderReference.Bartworks + ? BorosilicateGlass + .ofBoroGlass((byte) 0, (byte) 1, Byte.MAX_VALUE, (te, t) -> te.glasTier = t, te -> te.glasTier) + : onElementPass(t -> t.glasTier = 100, ofBlock(Blocks.glass, 0))) + .addElement( + 'd', + ofBlock( + LoaderReference.RandomThings ? Block.getBlockFromName("RandomThings:fertilizedDirt_tilled") + : Blocks.farmland, + 0)) + .addElement( + 'w', + 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<ItemStack> l = new ArrayList<>(2); + l.add(((GreenHouseSlot) s).input.copy()); + if (((GreenHouseSlot) s).undercrop != null) l.add(((GreenHouseSlot) s).undercrop.copy()); + return l; + }); + } + + @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."))); + } + } + + @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; + } + + @Override + public IMetaTileEntity newMetaEntity(IGregTechTileEntity iGregTechTileEntity) { + return new GT_MetaTileEntity_ExtremeIndustrialGreenhouse(this.mName); + } + + @Override + public IStructureDefinition<GT_MetaTileEntity_ExtremeIndustrialGreenhouse> getStructureDefinition() { + return STRUCTURE_DEFINITION; + } + + @Override + protected IAlignmentLimits getInitialAlignmentLimits() { + return (d, r, f) -> d.offsetY == 0 && r.isNotRotated() && f.isNotFlipped(); + } + + @Override + protected GT_Multiblock_Tooltip_Builder createTooltip() { + GT_Multiblock_Tooltip_Builder tt = new GT_Multiblock_Tooltip_Builder(); + tt.addMachineType("Crop Farm") + .addInfo("Controller block for the Extreme Industrial Greenhouse") + .addInfo(Author) + .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( + "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 --------------------") + .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) + .addSeparator() + .beginStructureBlock(5, 6, 5, false) + .addController("Front bottom center") + .addCasingInfoMin("Clean Stainless Steel Casings", 70, false) + .addOtherStructurePart("Borosilicate Glass", "Hollow two middle layers") + .addStructureInfo("The glass tier limits the Energy Input tier") + .addStructureInfo("The dirt is from RandomThings, must be tilled") + .addStructureInfo("Regular water and IC2 Distilled Water are accepted") + .addStructureInfo("Purple lamps are from ProjectRedIllumination. They can be powered and/or inverted") + .addMaintenanceHatch("Any casing (Except inner bottom ones)", 1) + .addInputBus("Any casing (Except inner bottom ones)", 1) + .addOutputBus("Any casing (Except inner bottom ones)", 1) + .addInputHatch("Any casing (Except inner bottom ones)", 1) + .addEnergyHatch("Any casing (Except inner bottom ones)", 1) + .toolTipFinisher(Tags.MODNAME); + return tt; + } + + @Override + public String[] getStructureDescription(ItemStack stackSize) { + List<String> info = new ArrayList<>(Arrays.asList(super.getStructureDescription(stackSize))); + info.add("The dirt is from RandomThings, must be tilled"); + info.add("Purple lamps are from ProjectRedIllumination. They can be powered and/or inverted"); + return info.toArray(new String[] {}); + } + + @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()); + } + + @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))); + } + + @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); + Minecraft.getMinecraft().effectRenderer.addEffect(crop); + } + + @Override + public void onPostTick(IGregTechTileEntity aBaseMetaTileEntity, long aTick) { + super.onPostTick(aBaseMetaTileEntity, aTick); + if (aBaseMetaTileEntity.isClientSide()) { + if (aBaseMetaTileEntity.isActive() && aTick % 40 == 0) { + int[] abc = new int[] { 0, -2, 2 }; + int[] xyz = new int[] { 0, 0, 0 }; + this.getExtendedFacing() + .getWorldOffset(abc, xyz); + xyz[0] += aBaseMetaTileEntity.getXCoord(); + xyz[1] += aBaseMetaTileEntity.getYCoord(); + xyz[2] += aBaseMetaTileEntity.getZCoord(); + spawnVisualCrops(aBaseMetaTileEntity.getWorld(), xyz[0], xyz[1], xyz[2], 40); + } + } + } + + @Override + public void construct(ItemStack itemStack, boolean b) { + buildPiece(STRUCTURE_PIECE_MAIN, itemStack, b, 2, 5, 0); + } + + 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); + } + + @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; + } + + if (setupphase > 0) { + if ((mStorage.size() >= mMaxSlots && setupphase == 1) || (mStorage.isEmpty() && setupphase == 2)) + return CheckRecipeResultRegistry.NO_RECIPE; + + if (setupphase == 1) { + List<ItemStack> inputs = getStoredInputs(); + for (ItemStack input : inputs) { + addCrop(input); + if (mStorage.size() >= mMaxSlots) break; + } + } else if (setupphase == 2) { + tryOutputAll(mStorage, s -> { + ArrayList<ItemStack> 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(); + this.mMaxProgresstime = 5; + this.lEUt = 0; + this.mEfficiency = (10000 - (getIdealStatus() - getRepairStatus()) * 1000); + 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<GT_MetaTileEntity_Hatch_Input> fluids = mInputHatches; + List<GT_MetaTileEntity_Hatch_Input> 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; + } + 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; + } + + // weedex + if (weedexusage > 0 && !this.depleteInput(new FluidStack(weedex, isIC2Mode ? weedexusage * 5 : weedexusage))) { + 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; + } + } + + // OVERCLOCK + // FERTILIZER IDEA: + // 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); + + ArrayList<ItemStack> 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); + i.stackSize -= used; + boost += used; + } + if (boost == maxboost) break; + } + + double multiplier = 1.d + (((double) boost / (double) maxboost) * 4d); + + if (isIC2Mode) { + if (glasTier < (EIG_BALANCE_IC2_ACCELERATOR_TIER + 1)) + return SimpleCheckRecipeResult.ofFailure("EIG_ic2glass"); + this.mMaxProgresstime = 100; + List<ItemStack> 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 { + this.mMaxProgresstime = Math.max(20, 100 / (tier - 3)); // Min 1 s + List<ItemStack> 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); + } + } + this.mOutputItems = outputs.toArray(new ItemStack[0]); + } + this.lEUt = -(int) ((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; + + boolean valid = this.mMaintenanceHatches.size() == 1 && !this.mEnergyHatches.isEmpty() && this.mCasing >= 70; + + if (valid) updateMaxSlots(); + + return valid; + } + + private static final UIInfo<?, ?> GreenhouseUI = createKTMetaTileEntityUI( + KT_ModulaUIContainer_ExtremeIndustrialGreenhouse::new); + + @Override + public boolean onRightclick(IGregTechTileEntity aBaseMetaTileEntity, EntityPlayer aPlayer) { + if (aBaseMetaTileEntity.isClientSide()) return true; + GreenhouseUI.open( + aPlayer, + aBaseMetaTileEntity.getWorld(), + aBaseMetaTileEntity.getXCoord(), + aBaseMetaTileEntity.getYCoord(), + aBaseMetaTileEntity.getZCoord()); + return true; + } + + private static class KT_ModulaUIContainer_ExtremeIndustrialGreenhouse extends ModularUIContainer { + + final WeakReference<GT_MetaTileEntity_ExtremeIndustrialGreenhouse> parent; + + public KT_ModulaUIContainer_ExtremeIndustrialGreenhouse(ModularUIContext context, ModularWindow mainWindow, + GT_MetaTileEntity_ExtremeIndustrialGreenhouse mte) { + super(context, mainWindow); + parent = new WeakReference<>(mte); + } + + @Override + public ItemStack transferStackInSlot(EntityPlayer aPlayer, int aSlotIndex) { + if (!(aPlayer instanceof EntityPlayerMP)) return super.transferStackInSlot(aPlayer, aSlotIndex); + final Slot s = getSlot(aSlotIndex); + if (s == null) return super.transferStackInSlot(aPlayer, aSlotIndex); + if (aSlotIndex >= 36) return super.transferStackInSlot(aPlayer, aSlotIndex); + final ItemStack aStack = s.getStack(); + 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.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); + } + } + + @Override + protected void addConfigurationWidgets(DynamicPositionedRow configurationElements, UIBuildContext buildContext) { + buildContext.addSyncedWindow(CONFIGURATION_WINDOW_ID, this::createConfigurationWindow); + configurationElements.setSynced(false); + configurationElements.widget( + new ButtonWidget().setOnClick( + (clickData, widget) -> { + if (!widget.isClient()) widget.getContext() + .openSyncedWindow(CONFIGURATION_WINDOW_ID); + }) + .setBackground(GT_UITextures.BUTTON_STANDARD, GT_UITextures.OVERLAY_BUTTON_CYCLIC) + .addTooltip("Configuration") + .setSize(16, 16)); + } + + DynamicInventory<GreenHouseSlot> dynamicInventory = new DynamicInventory<>( + 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; + } + }) + .setEnabled(() -> this.mMaxProgresstime == 0); + + @Override + public void createInventorySlots() { + + } + + private boolean isInInventory = true; + + @Override + public void addUIWidgets(ModularWindow.Builder builder, UIBuildContext buildContext) { + isInInventory = !getBaseMetaTileEntity().isActive(); + builder.widget( + new DrawableWidget().setDrawable(GT_UITextures.PICTURE_SCREEN_BLACK) + .setPos(4, 4) + .setSize(190, 85) + .setEnabled(w -> !isInInventory)); + 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")) + .setBackground(GT_UITextures.BUTTON_STANDARD) + .setPos(140, 4) + .setSize(55, 16)); + + final DynamicPositionedColumn screenElements = new DynamicPositionedColumn(); + drawTexts(screenElements, null); + builder.widget(screenElements.setEnabled(w -> !isInInventory)); + + builder.widget(createPowerSwitchButton(builder)) + .widget(createVoidExcessButton(builder)) + .widget(createInputSeparationButton(builder)) + .widget(createBatchModeButton(builder)) + .widget(createLockToSingleRecipeButton(builder)); + + DynamicPositionedRow configurationElements = new DynamicPositionedRow(); + addConfigurationWidgets(configurationElements, buildContext); + + builder.widget( + configurationElements.setSpace(2) + .setAlignment(MainAxisAlignment.SPACE_BETWEEN) + .setPos(getRecipeLockingButtonPos().add(18, 0))); + } + + protected ModularWindow createConfigurationWindow(final EntityPlayer player) { + ModularWindow.Builder builder = ModularWindow.builder(200, 100); + builder.setBackground(ModularUITextures.VANILLA_BACKGROUND); + builder.widget( + new DrawableWidget().setDrawable(GT_UITextures.OVERLAY_BUTTON_CYCLIC) + .setPos(5, 5) + .setSize(16, 16)) + .widget(new TextWidget("Configuration").setPos(25, 9)) + .widget( + ButtonWidget.closeWindowButton(true) + .setPos(185, 3)) + .widget( + new Column().widget( + new CycleButtonWidget().setLength(3) + .setGetter(() -> 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."))); + }) + .addTooltip(0, new Text("Operating").color(Color.GREEN.dark(3))) + .addTooltip(1, new Text("Input").color(Color.YELLOW.dark(3))) + .addTooltip(2, new Text("Output").color(Color.YELLOW.dark(3))) + .setTextureGetter( + i -> i == 0 ? new Text("Operating").color(Color.GREEN.dark(3)) + .withFixedSize(70 - 18, 18, 15, 0) + : i == 1 ? new Text("Input").color(Color.YELLOW.dark(3)) + .withFixedSize(70 - 18, 18, 15, 0) + : new Text("Output").color(Color.YELLOW.dark(3)) + .withFixedSize(70 - 18, 18, 15, 0)) + .setBackground( + ModularUITextures.VANILLA_BACKGROUND, + GT_UITextures.OVERLAY_BUTTON_CYCLIC.withFixedSize(18, 18)) + .setSize(70, 18) + .addTooltip("Setup mode")) + .widget( + new CycleButtonWidget().setLength(2) + .setGetter(() -> isIC2Mode ? 1 : 0) + .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.")); + }) + .addTooltip(0, new Text("Disabled").color(Color.RED.dark(3))) + .addTooltip(1, new Text("Enabled").color(Color.GREEN.dark(3))) + .setTextureGetter( + i -> i == 0 ? new Text("Disabled").color(Color.RED.dark(3)) + .withFixedSize(70 - 18, 18, 15, 0) + : new Text("Enabled").color(Color.GREEN.dark(3)) + .withFixedSize(70 - 18, 18, 15, 0)) + .setBackground( + ModularUITextures.VANILLA_BACKGROUND, + GT_UITextures.OVERLAY_BUTTON_CYCLIC.withFixedSize(18, 18)) + .setSize(70, 18) + .addTooltip("IC2 mode")) + .widget( + new CycleButtonWidget().setLength(2) + .setGetter(() -> isNoHumidity ? 1 : 0) + .setSetter(val -> { + if (!(player instanceof EntityPlayerMP)) return; + isNoHumidity = val == 1; + GT_Utility.sendChatToPlayer(player, "Give incoming crops no humidity " + isNoHumidity); + }) + .addTooltip(0, new Text("Disabled").color(Color.RED.dark(3))) + .addTooltip(1, new Text("Enabled").color(Color.GREEN.dark(3))) + .setTextureGetter( + i -> i == 0 ? new Text("Disabled").color(Color.RED.dark(3)) + .withFixedSize(70 - 18, 18, 15, 0) + : new Text("Enabled").color(Color.GREEN.dark(3)) + .withFixedSize(70 - 18, 18, 15, 0)) + .setBackground( + ModularUITextures.VANILLA_BACKGROUND, + GT_UITextures.OVERLAY_BUTTON_CYCLIC.withFixedSize(18, 18)) + .setSize(70, 18) + .addTooltip("No Humidity mode")) + .setEnabled(widget -> !getBaseMetaTileEntity().isActive()) + .setPos(10, 30)) + .widget( + new Column().widget(new TextWidget("Setup mode").setSize(100, 18)) + .widget(new TextWidget("IC2 mode").setSize(100, 18)) + .widget(new TextWidget("No Humidity mode").setSize(100, 18)) + .setEnabled(widget -> !getBaseMetaTileEntity().isActive()) + .setPos(80, 30)) + .widget( + new DrawableWidget().setDrawable(GT_UITextures.OVERLAY_BUTTON_CROSS) + .setSize(18, 18) + .setPos(10, 30) + .addTooltip(new Text("Can't change configuration when running !").color(Color.RED.dark(3))) + .setEnabled(widget -> getBaseMetaTileEntity().isActive())); + return builder.build(); + } + + private HashMap<ItemStack, Double> 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("s / ") + .append(String.format("%,.2f", (double) mMaxProgresstime / 20)) + .append("s (") + .append(String.format("%,.1f", (double) mProgresstime / mMaxProgresstime * 100)) + .append("%)\n"); + + for (Map.Entry<ItemStack, Double> drop : GUIDropProgress.entrySet()) { + int outputSize = Arrays.stream(mOutputItems) + .filter(s -> s.isItemEqual(drop.getKey())) + .mapToInt(i -> i.stackSize) + .sum(); + ret.append(EnumChatFormatting.AQUA) + .append( + drop.getKey() + .getDisplayName()) + .append(EnumChatFormatting.WHITE) + .append(": "); + if (outputSize == 0) { + ret.append(String.format("%.2f", drop.getValue() * 100)) + .append("%\n"); + } else { + ret.append(EnumChatFormatting.GOLD) + .append( + String.format( + "x%d %s(+%.2f/sec)\n", + outputSize, + EnumChatFormatting.WHITE, + (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<>(() -> { + HashMap<ItemStack, Double> ret = new HashMap<>(); + HashMap<String, Double> dropProgress = new HashMap<>(); + + for (Map.Entry<String, Double> drop : dropprogress.entrySet()) { + dropProgress.merge(drop.getKey(), drop.getValue(), Double::sum); + } + + for (Map.Entry<String, Double> drop : dropProgress.entrySet()) { + ret.put(GreenHouseSlot.dropstacks.get(drop.getKey()), drop.getValue()); + } + return ret; + }, h -> GUIDropProgress = h, (buffer, h) -> { + buffer.writeVarIntToBuffer(h.size()); + for (Map.Entry<ItemStack, Double> itemStackDoubleEntry : h.entrySet()) { + try { + buffer.writeItemStackToBuffer(itemStackDoubleEntry.getKey()); + buffer.writeDouble(itemStackDoubleEntry.getValue()); + } catch (IOException e) { + throw new RuntimeException(e); + } + } + }, buffer -> { + int len = buffer.readVarIntFromBuffer(); + HashMap<ItemStack, Double> ret = new HashMap<>(len); + for (int i = 0; i < len; i++) { + try { + ret.put(buffer.readItemStackFromBuffer(), buffer.readDouble()); + } catch (IOException e) { + throw new RuntimeException(e); + } + } + return ret; + })); + super.drawTexts(screenElements, inventorySlot); + } + + @Override + public String[] getInfoData() { + List<String> info = new ArrayList<>( + Arrays.asList( + "Running in mode: " + EnumChatFormatting.GREEN + + (setupphase == 0 ? (isIC2Mode ? "IC2 crops" : "Normal crops") + : ("Setup mode " + (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() + + EnumChatFormatting.RESET)); + HashMap<String, Integer> storageList = new HashMap<>(); + for (GreenHouseSlot greenHouseSlot : mStorage) { + if (!greenHouseSlot.isValid) continue; + StringBuilder a = new StringBuilder( + EnumChatFormatting.GREEN + "x" + + greenHouseSlot.input.stackSize + + " " + + greenHouseSlot.input.getDisplayName()); + if (this.isIC2Mode) { + a.append(" | Humidity: ") + .append(greenHouseSlot.noHumidity ? 0 : 12); + } + a.append(EnumChatFormatting.RESET); + storageList.merge(a.toString(), 1, Integer::sum); + } + storageList.forEach((k, v) -> info.add("x" + v + " " + k)); + if (mStorage.size() > mMaxSlots) info + .add(EnumChatFormatting.DARK_RED + "There are too many crops inside to run !" + EnumChatFormatting.RESET); + info.addAll(Arrays.asList(super.getInfoData())); + return info.toArray(new String[0]); + } + + @Override + public ITexture[] getTexture(IGregTechTileEntity aBaseMetaTileEntity, ForgeDirection side, ForgeDirection facing, + int colorIndex, boolean aActive, boolean aRedstone) { + if (side == facing) { + if (aActive) return new ITexture[] { Textures.BlockIcons.getCasingTextureForId(CASING_INDEX), + TextureFactory.builder() + .addIcon(OVERLAY_FRONT_DISTILLATION_TOWER_ACTIVE) + .extFacing() + .build(), + TextureFactory.builder() + .addIcon(OVERLAY_FRONT_DISTILLATION_TOWER_ACTIVE_GLOW) + .extFacing() + .glow() + .build() }; + return new ITexture[] { Textures.BlockIcons.getCasingTextureForId(CASING_INDEX), TextureFactory.builder() + .addIcon(OVERLAY_FRONT_DISTILLATION_TOWER) + .extFacing() + .build(), + TextureFactory.builder() + .addIcon(OVERLAY_FRONT_DISTILLATION_TOWER_GLOW) + .extFacing() + .glow() + .build() }; + } + return new ITexture[] { Textures.BlockIcons.getCasingTextureForId(CASING_INDEX) }; + } + + private boolean addCrop(ItemStack input, int slot, boolean simulate) { + if (!isIC2Mode && !simulate) + for (GreenHouseSlot g : mStorage) if (g.input.stackSize < 64 && GT_Utility.areStacksEqual(g.input, input)) { + g.addAll( + this.getBaseMetaTileEntity() + .getWorld(), + input); + if (input.stackSize == 0) return true; + } + GreenHouseSlot h = new GreenHouseSlot(this, simulate ? input.copy() : input, isIC2Mode, isNoHumidity); + if (h.isValid) { + if (!simulate) { + if (slot == -1) mStorage.add(h); + else mStorage.add(slot, h); + } + return true; + } + return false; + } + + private ItemStack addCrop(ItemStack input) { + if (addCrop(input, -1, false)) return input; + return null; + } + + final Map<String, Double> dropprogress = new HashMap<>(); + + public static class GreenHouseSlot extends InventoryCrafting { + + private static final int NUMBER_OF_GENERATIONS_TO_MAKE = 10; + + final ItemStack input; + Block crop; + ArrayList<ItemStack> customDrops = null; + ItemStack undercrop = null; + List<ItemStack> drops; + public boolean isValid; + boolean isIC2Crop; + boolean noHumidity; + int growthticks; + List<List<ItemStack>> generations; + + Random rn; + IRecipe recipe; + ItemStack recipeInput; + + int optimalgrowth = 7; + + boolean needsreplanting = true; + + static final GreenHouseWorld fakeworld = new GreenHouseWorld(5, 5, 5); + + public NBTTagCompound toNBTTagCompound() { + NBTTagCompound aNBT = new NBTTagCompound(); + aNBT.setTag("input", writeItemStackToNBT(input)); + aNBT.setBoolean("isValid", isValid); + aNBT.setBoolean("isIC2Crop", isIC2Crop); + if (!isIC2Crop) { + aNBT.setInteger("crop", Block.getIdFromBlock(crop)); + if (customDrops != null && customDrops.size() > 0) { + aNBT.setInteger("customDropsCount", customDrops.size()); + for (int i = 0; i < customDrops.size(); i++) + aNBT.setTag("customDrop." + i, writeItemStackToNBT(customDrops.get(i))); + } + aNBT.setInteger("dropscount", drops.size()); + for (int i = 0; i < drops.size(); i++) aNBT.setTag("drop." + i, writeItemStackToNBT(drops.get(i))); + aNBT.setInteger("optimalgrowth", optimalgrowth); + aNBT.setBoolean("needsreplanting", needsreplanting); + } else { + if (undercrop != null) aNBT.setTag("undercrop", writeItemStackToNBT(undercrop)); + aNBT.setInteger("generationscount", generations.size()); + for (int i = 0; i < generations.size(); i++) { + aNBT.setInteger( + "generation." + i + ".count", + generations.get(i) + .size()); + for (int j = 0; j < generations.get(i) + .size(); j++) + aNBT.setTag( + "generation." + i + "." + j, + writeItemStackToNBT( + generations.get(i) + .get(j))); + } + aNBT.setInteger("growthticks", growthticks); + aNBT.setBoolean("noHumidity", noHumidity); + } + return aNBT; + } + + public GreenHouseSlot(NBTTagCompound aNBT) { + super(null, 3, 3); + isIC2Crop = aNBT.getBoolean("isIC2Crop"); + isValid = aNBT.getBoolean("isValid"); + input = readItemStackFromNBT(aNBT.getCompoundTag("input")); + if (!isIC2Crop) { + crop = Block.getBlockById(aNBT.getInteger("crop")); + if (aNBT.hasKey("customDropsCount")) { + int imax = aNBT.getInteger("customDropsCount"); + customDrops = new ArrayList<>(imax); + for (int i = 0; i < imax; i++) + customDrops.add(readItemStackFromNBT(aNBT.getCompoundTag("customDrop." + i))); + } + drops = new ArrayList<>(); + for (int i = 0; i < aNBT.getInteger("dropscount"); i++) + drops.add(readItemStackFromNBT(aNBT.getCompoundTag("drop." + i))); + optimalgrowth = aNBT.getInteger("optimalgrowth"); + if (optimalgrowth == 0) optimalgrowth = 7; + if (aNBT.hasKey("needsreplanting")) needsreplanting = aNBT.getBoolean("needsreplanting"); + } else { + if (aNBT.hasKey("undercrop")) undercrop = readItemStackFromNBT(aNBT.getCompoundTag("undercrop")); + generations = new ArrayList<>(); + for (int i = 0; i < aNBT.getInteger("generationscount"); i++) { + generations.add(new ArrayList<>()); + for (int j = 0; j < aNBT.getInteger("generation." + i + ".count"); j++) generations.get(i) + .add(readItemStackFromNBT(aNBT.getCompoundTag("generation." + i + "." + j))); + } + growthticks = aNBT.getInteger("growthticks"); + noHumidity = aNBT.getBoolean("noHumidity"); + rn = new Random(); + } + } + + public boolean addAll(World world, ItemStack input) { + if (!GT_Utility.areStacksEqual(this.input, input)) return false; + if (this.input.stackSize == 64) return false; + int toconsume = Math.min(64 - this.input.stackSize, input.stackSize); + int left = addDrops(world, toconsume); + input.stackSize -= toconsume - left; + this.input.stackSize += toconsume - left; + return left == 0; + } + + public boolean findCropRecipe(World world) { + if (recipe != null) return true; + out: for (ItemStack drop : drops) { + recipeInput = drop; + for (int j = 0; j < CraftingManager.getInstance() + .getRecipeList() + .size(); j++) { + recipe = (IRecipe) CraftingManager.getInstance() + .getRecipeList() + .get(j); + if (recipe.matches(this, world) + && GT_Utility.areStacksEqual(recipe.getCraftingResult(this), input)) { + break out; + } else recipe = null; + } + } + return recipe != null; + } + + @Override + public ItemStack getStackInSlot(int p_70301_1_) { + if (p_70301_1_ == 0) return recipeInput.copy(); + return null; + } + + @Override + public ItemStack getStackInSlotOnClosing(int par1) { + return null; + } + + @Override + public ItemStack decrStackSize(int par1, int par2) { + return null; + } + + @SuppressWarnings("EmptyMethod") + @Override + public void setInventorySlotContents(int par1, ItemStack par2ItemStack) {} + + public GreenHouseSlot(GT_MetaTileEntity_ExtremeIndustrialGreenhouse tileEntity, ItemStack input, boolean IC2, + boolean noHumidity) { + super(null, 3, 3); + World world = tileEntity.getBaseMetaTileEntity() + .getWorld(); + this.input = input.copy(); + this.isValid = false; + if (IC2) { + GreenHouseSlotIC2(tileEntity, world, input, noHumidity); + return; + } + Item i = input.getItem(); + Block b = null; + boolean detectedCustomHandler = false; + // Custom handlers + // FLOWERS // + Block bb = Block.getBlockFromItem(i); + if (bb == Blocks.air) bb = null; + if (bb instanceof BlockFlower) { + detectedCustomHandler = true; + needsreplanting = false; + customDrops = new ArrayList<>(Collections.singletonList(input.copy())); + customDrops.get(0).stackSize = 1; + } + if (!detectedCustomHandler) { + if (i instanceof IPlantable) { + if (i instanceof ItemSeeds) b = ((ItemSeeds) i).getPlant(world, 0, 0, 0); + else if (i instanceof ItemSeedFood) b = ((ItemSeedFood) i).getPlant(world, 0, 0, 0); + } else { + if (i == Items.reeds) b = Blocks.reeds; + else { + b = Block.getBlockFromItem(i); + if (b != Blocks.cactus) return; + } + needsreplanting = false; + } + if (!(b instanceof IPlantable)) return; + GameRegistry.UniqueIdentifier u = GameRegistry.findUniqueIdentifierFor(i); + if (u != null && Objects.equals(u.modId, "Natura")) optimalgrowth = 8; + + if (b instanceof BlockStem) { + fakeworld.block = null; + try { + b.updateTick(fakeworld, 5, 5, 5, fakeworld.rand); + } catch (Exception e) { + e.printStackTrace(System.err); + } + if (fakeworld.block == null) return; + b = fakeworld.block; + needsreplanting = false; + } + } + crop = b; + isIC2Crop = false; + int toUse = Math.min(64, input.stackSize); + if (addDrops(world, toUse) == 0 && !drops.isEmpty()) { + input.stackSize -= toUse; + this.input.stackSize = toUse; + this.isValid = true; + } + } + + public void GreenHouseSlotIC2(GT_MetaTileEntity_ExtremeIndustrialGreenhouse tileEntity, World world, + ItemStack input, boolean noHumidity) { + if (!ItemList.IC2_Crop_Seeds.isStackEqual(input, true, true)) return; + this.isIC2Crop = true; + this.noHumidity = noHumidity; + recalculate(tileEntity, world); + if (this.isValid) input.stackSize--; + } + + private boolean setBlock(ItemStack a, int x, int y, int z, World world) { + Item item = a.getItem(); + Block b = Block.getBlockFromItem(item); + if (b == Blocks.air || !(item instanceof ItemBlock)) return false; + short tDamage = (short) item.getDamage(a); + if (item instanceof GT_Item_Ores && tDamage > 0) { + if (!world.setBlock( + x, + y, + z, + b, + GT_TileEntity_Ores.getHarvestData( + tDamage, + ((GT_Block_Ores_Abstract) b).getBaseBlockHarvestLevel(tDamage % 16000 / 1000)), + 0)) { + return false; + } + GT_TileEntity_Ores tTileEntity = (GT_TileEntity_Ores) world.getTileEntity(x, y, z); + tTileEntity.mMetaData = tDamage; + tTileEntity.mNatural = false; + } else world.setBlock(x, y, z, b, tDamage, 0); + return true; + } + + public void recalculate(GT_MetaTileEntity_ExtremeIndustrialGreenhouse tileEntity, World world) { + if (isIC2Crop) { + CropCard cc = Crops.instance.getCropCard(input); + this.input.stackSize = 1; + NBTTagCompound nbt = input.getTagCompound(); + byte gr = nbt.getByte("growth"); + byte ga = nbt.getByte("gain"); + byte re = nbt.getByte("resistance"); + int[] abc = new int[] { 0, -2, 3 }; + int[] xyz = new int[] { 0, 0, 0 }; + tileEntity.getExtendedFacing() + .getWorldOffset(abc, xyz); + xyz[0] += tileEntity.getBaseMetaTileEntity() + .getXCoord(); + xyz[1] += tileEntity.getBaseMetaTileEntity() + .getYCoord(); + xyz[2] += tileEntity.getBaseMetaTileEntity() + .getZCoord(); + boolean cheating = false; + try { + if (world.getBlock(xyz[0], xyz[1] - 2, xyz[2]) != GregTech_API.sBlockCasings4 + || world.getBlockMetadata(xyz[0], xyz[1] - 2, xyz[2]) != 1) { + // no + cheating = true; + return; + } + + world.setBlock(xyz[0], xyz[1], xyz[2], Block.getBlockFromItem(Ic2Items.crop.getItem()), 0, 0); + TileEntity wte = world.getTileEntity(xyz[0], xyz[1], xyz[2]); + if (!(wte instanceof TileEntityCrop)) { + // should not be even possible + return; + } + TileEntityCrop te = (TileEntityCrop) wte; + te.ticker = 1; // don't even think about ticking once + te.setCrop(cc); + + te.setGrowth(gr); + te.setGain(ga); + te.setResistance(re); + + if (noHumidity) te.humidity = 0; + else { + te.waterStorage = 200; + te.humidity = te.updateHumidity(); + } + te.airQuality = te.updateAirQuality(); + te.nutrients = te.updateNutrients(); + + ItemStack tobeused = null; + + if (undercrop != null) setBlock(undercrop, xyz[0], xyz[1] - 2, xyz[2], world); + else { + te.setSize((byte) (cc.maxSize() - 1)); + if (!cc.canGrow(te)) { + // needs special block + + boolean cangrow = false; + ArrayList<ItemStack> inputs = tileEntity.getStoredInputs(); + for (ItemStack a : inputs) { + if (a.stackSize <= 0) continue; + if (!setBlock(a, xyz[0], xyz[1] - 2, xyz[2], world)) continue; + if (!cc.canGrow(te)) continue; + cangrow = true; + undercrop = a.copy(); + undercrop.stackSize = 1; + tobeused = a; + break; + } + + if (!cangrow) return; + } + } + + te.setSize((byte) cc.maxSize()); + + if (!cc.canBeHarvested(te)) return; + + // GENERATE DROPS + generations = new ArrayList<>(); + int afterHarvestCropSize = 0; + out: for (int i = 0; i < NUMBER_OF_GENERATIONS_TO_MAKE; i++) // get 10 generations + { + ItemStack[] st = te.harvest_automated(false); + afterHarvestCropSize = te.getSize(); + te.setSize((byte) cc.maxSize()); + if (st == null) continue; + if (st.length == 0) continue; + for (ItemStack s : st) if (s == null) continue out; + generations.add(new ArrayList<>(Arrays.asList(st))); + } + if (generations.isEmpty()) return; + rn = new Random(); + + // CHECK GROWTH SPEED + + growthticks = 0; + + for (int i = afterHarvestCropSize; i < cc.maxSize(); i++) { + te.setSize((byte) i); + int grown = 0; + do { + int rate = te.calcGrowthRate(); + if (rate == 0) return; + growthticks++; + grown += rate; + } while (grown < cc.growthDuration(te)); + } + + growthticks *= TileEntityCrop.tickRate; + if (growthticks < 1) growthticks = 1; + + if (tobeused != null) tobeused.stackSize--; + + this.isValid = true; + } catch (Exception e) { + e.printStackTrace(System.err); + } finally { + if (!cheating) world.setBlock(xyz[0], xyz[1] - 2, xyz[2], GregTech_API.sBlockCasings4, 1, 0); + world.setBlockToAir(xyz[0], xyz[1], xyz[2]); + } + } else { + drops = new ArrayList<>(); + addDrops(world, input.stackSize); + } + } + + public List<ItemStack> getDrops() { + return drops; + } + + static final Map<String, ItemStack> dropstacks = new HashMap<>(); + + public List<ItemStack> getIC2Drops(GT_MetaTileEntity_ExtremeIndustrialGreenhouse tileEntity, + double timeelapsed) { + int r = rn.nextInt(NUMBER_OF_GENERATIONS_TO_MAKE); + if (generations.size() <= r) return new ArrayList<>(); + double growthPercent = (timeelapsed / (double) growthticks); + List<ItemStack> generation = generations.get(r); + List<ItemStack> copied = new ArrayList<>(); + for (ItemStack g : generation) copied.add(g.copy()); + for (ItemStack s : copied) { + double pro = ((double) s.stackSize * growthPercent); + s.stackSize = 1; + tileEntity.dropprogress.merge(s.toString(), pro, Double::sum); + if (!dropstacks.containsKey(s.toString())) dropstacks.put(s.toString(), s.copy()); + } + copied.clear(); + for (Map.Entry<String, Double> entry : tileEntity.dropprogress.entrySet()) if (entry.getValue() >= 1d) { + copied.add( + dropstacks.get(entry.getKey()) + .copy()); + copied.get(copied.size() - 1).stackSize = entry.getValue() + .intValue(); + entry.setValue( + entry.getValue() - (double) entry.getValue() + .intValue()); + } + return copied; + } + + public int addDrops(World world, int count) { + if (drops == null) drops = new ArrayList<>(); + if (customDrops != null && !customDrops.isEmpty()) { + @SuppressWarnings("unchecked") + ArrayList<ItemStack> d = (ArrayList<ItemStack>) customDrops.clone(); + for (ItemStack x : drops) { + for (Iterator<ItemStack> iterator = d.iterator(); iterator.hasNext();) { + ItemStack y = iterator.next(); + if (GT_Utility.areStacksEqual(x, y)) { + x.stackSize += y.stackSize * count; + iterator.remove(); + } + } + } + final int finalCount = count; + d.forEach(stack -> { + ItemStack i = stack.copy(); + i.stackSize *= finalCount; + drops.add(i); + }); + return 0; + } else { + if (crop == null) return count; + for (int i = 0; i < count; i++) { + List<ItemStack> d = crop.getDrops(world, 0, 0, 0, optimalgrowth, 0); + for (ItemStack x : drops) for (ItemStack y : d) if (GT_Utility.areStacksEqual(x, y)) { + x.stackSize += y.stackSize; + y.stackSize = 0; + } + for (ItemStack x : d) if (x.stackSize > 0) drops.add(x.copy()); + } + } + if (!needsreplanting) return 0; + for (int i = 0; i < drops.size(); i++) { + if (GT_Utility.areStacksEqual(drops.get(i), input)) { + int took = Math.min(drops.get(i).stackSize, count); + drops.get(i).stackSize -= took; + count -= took; + if (drops.get(i).stackSize == 0) { + drops.remove(i); + i--; + } + if (count == 0) { + return 0; + } + } + } + if (!findCropRecipe(world)) return count; + int totake = count / recipe.getCraftingResult(this).stackSize + 1; + for (int i = 0; i < drops.size(); i++) { + if (GT_Utility.areStacksEqual(drops.get(i), recipeInput)) { + int took = Math.min(drops.get(i).stackSize, totake); + drops.get(i).stackSize -= took; + totake -= took; + if (drops.get(i).stackSize == 0) { + drops.remove(i); + i--; + } + if (totake == 0) { + return 0; + } + } + } + return count; + } + } + + private static class GreenHouseWorld extends GT_DummyWorld { + + public int x, y, z, meta = 0; + public Block block; + + GreenHouseWorld(int x, int y, int z) { + super(); + this.x = x; + this.y = y; + this.z = z; + this.rand = new GreenHouseRandom(); + } + + @Override + public int getBlockMetadata(int aX, int aY, int aZ) { + if (aX == x && aY == y && aZ == z) return 7; + return 0; + } + + @Override + public Block getBlock(int aX, int aY, int aZ) { + if (aY == y - 1) return Blocks.farmland; + return Blocks.air; + } + + @Override + public int getBlockLightValue(int p_72957_1_, int p_72957_2_, int p_72957_3_) { + return 10; + } + + @Override + public boolean setBlock(int aX, int aY, int aZ, Block aBlock, int aMeta, int aFlags) { + if (aBlock == Blocks.air) return false; + if (aX == x && aY == y && aZ == z) return false; + block = aBlock; + meta = aMeta; + return true; + } + } + + private static class GreenHouseRandom extends Random { + + private static final long serialVersionUID = -387271808935248890L; + + @Override + public int nextInt(int bound) { + return 0; + } + } +} diff --git a/src/main/java/kubatech/tileentity/gregtech/multiblock/GT_MetaTileEntity_MegaIndustrialApiary.java b/src/main/java/kubatech/tileentity/gregtech/multiblock/GT_MetaTileEntity_MegaIndustrialApiary.java new file mode 100644 index 0000000000..8a8dd6d4fc --- /dev/null +++ b/src/main/java/kubatech/tileentity/gregtech/multiblock/GT_MetaTileEntity_MegaIndustrialApiary.java @@ -0,0 +1,1179 @@ +/* + * spotless:off + * KubaTech - Gregtech Addon + * Copyright (C) 2022 - 2024 kuba6000 + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this library. If not, see <https://www.gnu.org/licenses/>. + * spotless:on + */ + +package kubatech.tileentity.gregtech.multiblock; + +import static com.gtnewhorizon.structurelib.structure.StructureUtility.ofBlock; +import static com.gtnewhorizon.structurelib.structure.StructureUtility.ofBlockAnyMeta; +import static com.gtnewhorizon.structurelib.structure.StructureUtility.ofBlocksMap; +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 forestry.api.apiculture.BeeManager.beeRoot; +import static gregtech.api.enums.GT_HatchElement.Energy; +import static gregtech.api.enums.GT_HatchElement.InputBus; +import static gregtech.api.enums.GT_HatchElement.Maintenance; +import static gregtech.api.enums.GT_HatchElement.OutputBus; +import static gregtech.api.enums.Textures.BlockIcons.OVERLAY_FRONT_DISTILLATION_TOWER; +import static gregtech.api.enums.Textures.BlockIcons.OVERLAY_FRONT_DISTILLATION_TOWER_ACTIVE; +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.buildHatchAdder; +import static kubatech.api.Variables.StructureHologram; +import static kubatech.api.Variables.buildAuthorList; +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.HashMap; +import java.util.HashSet; +import java.util.List; +import java.util.Map; +import java.util.stream.Collectors; +import java.util.stream.IntStream; + +import net.minecraft.client.Minecraft; +import net.minecraft.entity.player.EntityPlayer; +import net.minecraft.entity.player.EntityPlayerMP; +import net.minecraft.init.Blocks; +import net.minecraft.inventory.Slot; +import net.minecraft.item.ItemStack; +import net.minecraft.nbt.NBTTagCompound; +import net.minecraft.util.EnumChatFormatting; +import net.minecraft.world.World; +import net.minecraftforge.common.util.ForgeDirection; + +import org.jetbrains.annotations.NotNull; + +import com.github.bartimaeusnek.bartworks.API.BorosilicateGlass; +import com.gtnewhorizon.structurelib.StructureLibAPI; +import com.gtnewhorizon.structurelib.alignment.IAlignmentLimits; +import com.gtnewhorizon.structurelib.alignment.constructable.ISurvivalConstructable; +import com.gtnewhorizon.structurelib.structure.IStructureDefinition; +import com.gtnewhorizon.structurelib.structure.IStructureElementNoPlacement; +import com.gtnewhorizon.structurelib.structure.ISurvivalBuildEnvironment; +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; +import com.gtnewhorizons.modularui.api.screen.ModularWindow; +import com.gtnewhorizons.modularui.api.screen.UIBuildContext; +import com.gtnewhorizons.modularui.common.builder.UIInfo; +import com.gtnewhorizons.modularui.common.internal.wrapper.ModularUIContainer; +import com.gtnewhorizons.modularui.common.widget.ButtonWidget; +import com.gtnewhorizons.modularui.common.widget.Column; +import com.gtnewhorizons.modularui.common.widget.CycleButtonWidget; +import com.gtnewhorizons.modularui.common.widget.DrawableWidget; +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.SlotWidget; +import com.gtnewhorizons.modularui.common.widget.TextWidget; +import com.kuba6000.mobsinfo.api.utils.ItemID; + +import cpw.mods.fml.relauncher.Side; +import cpw.mods.fml.relauncher.SideOnly; +import forestry.api.apiculture.EnumBeeType; +import forestry.api.apiculture.FlowerManager; +import forestry.api.apiculture.IAlleleBeeSpecies; +import forestry.api.apiculture.IBee; +import forestry.api.apiculture.IBeeGenome; +import forestry.api.apiculture.IBeeModifier; +import forestry.api.apiculture.IBeekeepingMode; +import forestry.apiculture.blocks.BlockAlveary; +import forestry.apiculture.blocks.BlockApicultureType; +import forestry.apiculture.genetics.Bee; +import forestry.plugins.PluginApiculture; +import gregtech.api.GregTech_API; +import gregtech.api.enums.GT_Values; +import gregtech.api.enums.Textures; +import gregtech.api.gui.modularui.GT_UITextures; +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.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 ic2.core.init.BlocksItems; +import ic2.core.init.InternalName; +import kubatech.Tags; +import kubatech.api.DynamicInventory; +import kubatech.api.LoaderReference; +import kubatech.api.implementations.KubaTechGTMultiBlockBase; +import kubatech.client.effect.MegaApiaryBeesRenderer; + +public class GT_MetaTileEntity_MegaIndustrialApiary + extends KubaTechGTMultiBlockBase<GT_MetaTileEntity_MegaIndustrialApiary> implements ISurvivalConstructable { + + private byte mGlassTier = 0; + private int mCasing = 0; + private int mMaxSlots = 0; + private int mPrimaryMode = 0; + private int mSecondaryMode = 0; + private final ArrayList<BeeSimulator> mStorage = new ArrayList<>(); + + private static final ItemStack royalJelly = PluginApiculture.items.royalJelly.getItemStack(1); + private static final int CASING_INDEX = 10; + private static final String STRUCTURE_PIECE_MAIN = "main"; + private static final String STRUCTURE_PIECE_MAIN_SURVIVAL = "mainsurvival"; + private static final int CONFIGURATION_WINDOW_ID = 999; + private static final int MEGA_APIARY_STORAGE_VERSION = 2; + + private static final String[][] struct = transpose( + new String[][] { // spotless:off + {" "," "," "," HHH "," HHAAAHH "," HAPLPAH "," HAPAAAPAH "," HALAAALAH "," HAPAAAPAH "," HAPLPAH "," HHAAAHH "," HHH "," "," "," "}, + {" "," "," GGG "," GGG GG "," G G "," G G "," G G "," G G "," G G "," G G "," G G "," GG GG "," GGG "," "," "}, + {" "," HHH "," HHH HHH "," H GH "," H H "," H H "," H H "," H H "," H H "," H H "," H H "," HG GH "," HHH HHH "," HHH "," "}, + {" GGG "," GGG GGG "," G G "," G G "," G G "," G G ","G G","G G","G G"," G G "," G G "," G G "," G G "," GGG GGG "," GGG "}, + {" AAA "," OLA ALO "," P P "," O O "," L L "," A A ","A A","A A","A A"," A A "," L L "," O O "," P P "," OLA ALO "," AAA "}, + {" AAAAA "," NA AO "," P P "," N O "," A A ","A A","A III A","A III A","A III A","A A"," A A "," N N "," P P "," NA AN "," AAAAA "}, + {" AAAAA "," NA FFF AO "," PFF FFP "," NF FFO "," AF FA ","A A","AF JJJ FA","AF JKJ FA","AF JJJ FA","A A"," AF FA "," NFF FFN "," PFF FFP "," NA FFF AN "," AAAAA "}, + {" AAA "," OLAFFFALO "," PFFFFFFFFFP "," OFFFF FFFFO "," LFF FFL "," AFF FFFFF FA ","AFF FKKKFF FFA","AFF FFKKKFF FFA","AFF FFKKKF FFA"," AF FFFFF FA "," LFF FF FFL "," OFFFF FFFO "," PFFFFFFFFFP "," OLAFFFALO "," AAA "}, + {" G~G "," GGGBBBGGG "," GBBFFFFFBBG "," GBFFF FFBBG "," GBF FBG "," GFF FFFFF FG ","GBF FKKKFF FBG","GBF FFKJKFF FBG","GBF FFKKKF FBG"," GF FFFFF FG "," GBF FF FBG "," GBBFF FBBG "," GBBFFFFFBBG "," GGGBBBGGG "," GGG "}, + {" HHH "," HHBBBHH "," HHBBBBBBBHH "," HBBBWWWBBBH "," HBBWWWWWWWBBH "," HBBWBBBBBWWBH ","HBBWWBBBBBBWBBH","HBBWBBBBBBBWBBH","HBBWBBBBBBWWBBH"," HBWWBBBBBWWBH "," HBBWWWBBWWBBH "," HBBBWWWWBBH "," HHBBBBBBBHH "," HHBBBHH "," HHH "}, + {" "," GGGGG "," GGGBBBBGG "," GBBBBBBBBBG "," GBBBBBBBBBG "," GBBBBBBBBBBBG "," GBBBBBBBBBBBG "," GBBBBBBBBBBBG "," GBBBBBBBBBBBG "," GBBBBBBBBBBBG "," GBBBBBBBBBG "," GBBBBBBBBBG "," GGBBBBBGG "," GGGGG "," "}, + {" "," HHH "," HHBBBHH "," HBBBBBBBH "," HBBBBBBBBBH "," HBBBBBBBBBH "," HBBBBBBBBBBBH "," HBBBBBBBBBBBH "," HBBBBBBBBBBBH "," HBBBBBBBBBH "," HBBBBBBBBBH "," HBBBBBBBH "," HHBBBHH "," HHH "," "}, + {" "," "," GGG "," GGBBBGG "," GBBBBBBBG "," GBBBBBBBG "," GBBBBBBBBBG "," GBBBBBBBBBG "," GBBBBBBBBBG "," GBBBBBBBG "," GBBBBBBBG "," GGBBBGG "," GGG "," "," "}, + {" "," "," H "," HHBHH "," HBBBBBH "," HBBBBBBBH "," HBBBBBBBH "," HBBBBBBBBBH "," HBBBBBBBH "," HBBBBBBBH "," HBBBBBH "," HHBHH "," H "," "," "}, + {" "," "," "," G "," GGBGG "," GBBBBBG "," GBBBBBG "," GBBBBBBBG "," GBBBBBG "," GBBBBBG "," GGBGG "," G "," "," "," "}, + {" "," "," "," "," HHH "," HHHHH "," HHBBBHH "," HHBBBHH "," HHBBBHH "," HHBHH "," HHH "," "," "," "," "}, + {" "," "," "," "," "," "," GGG "," GHG "," GGG "," "," "," "," "," "," "} + }); // spotless:on + + private static final IStructureDefinition<GT_MetaTileEntity_MegaIndustrialApiary> STRUCTURE_DEFINITION = StructureDefinition + .<GT_MetaTileEntity_MegaIndustrialApiary>builder() + .addShape(STRUCTURE_PIECE_MAIN, struct) + .addShape( + STRUCTURE_PIECE_MAIN_SURVIVAL, + Arrays.stream(struct) + .map( + sa -> Arrays.stream(sa) + .map( + s -> s.replaceAll("W", " ") + .replaceAll("F", " ")) + .toArray(String[]::new)) + .toArray(String[][]::new)) + .addElement( + 'A', + LoaderReference.Bartworks + ? BorosilicateGlass.ofBoroGlass((byte) 0, (t, v) -> t.mGlassTier = v, t -> t.mGlassTier) + : onElementPass(t -> t.mGlassTier = 100, ofBlock(Blocks.glass, 0))) + .addElement('B', ofChain(ofBlockAnyMeta(Blocks.dirt, 0), ofBlock(Blocks.grass, 0))) + .addElement( + 'G', + buildHatchAdder(GT_MetaTileEntity_MegaIndustrialApiary.class) + .atLeast(InputBus, OutputBus, Energy, Maintenance) + .casingIndex(CASING_INDEX) + .dot(1) + .buildAndChain(onElementPass(t -> t.mCasing++, ofBlock(GregTech_API.sBlockCasings1, 10)))) + .addElement( + 'H', + ofBlocksMap( + Collections.singletonMap( + Blocks.planks, + IntStream.rangeClosed(0, 5) + .boxed() + .collect(Collectors.toList())), + Blocks.planks, + 5)) + .addElement( + 'I', + ofBlocksMap( + Collections.singletonMap( + Blocks.wooden_slab, + IntStream.rangeClosed(0, 5) + .boxed() + .collect(Collectors.toList())), + Blocks.wooden_slab, + 5)) + .addElement('J', ofBlock(PluginApiculture.blocks.apiculture, BlockApicultureType.APIARY.getMeta())) + .addElement('K', ofBlock(PluginApiculture.blocks.alveary, BlockAlveary.Type.PLAIN.ordinal())) + .addElement('L', ofBlock(PluginApiculture.blocks.alveary, BlockAlveary.Type.HYGRO.ordinal())) + .addElement('N', ofBlock(PluginApiculture.blocks.alveary, BlockAlveary.Type.STABILIZER.ordinal())) + .addElement('O', ofBlock(PluginApiculture.blocks.alveary, BlockAlveary.Type.HEATER.ordinal())) + .addElement('P', ofBlock(PluginApiculture.blocks.alveary, BlockAlveary.Type.FAN.ordinal())) + .addElement( + 'W', + ofChain(ofBlock(Blocks.water, 0), ofBlock(BlocksItems.getFluidBlock(InternalName.fluidDistilledWater), 0))) + .addElement('F', new IStructureElementNoPlacement<GT_MetaTileEntity_MegaIndustrialApiary>() { + + @Override + public boolean check(GT_MetaTileEntity_MegaIndustrialApiary mte, World world, int x, int y, int z) { + mte.flowerCheck(world, x, y, z); + return true; + } + + @Override + public boolean spawnHint(GT_MetaTileEntity_MegaIndustrialApiary mte, World world, int x, int y, int z, + ItemStack trigger) { + StructureLibAPI.hintParticle(world, x, y, z, StructureLibAPI.getBlockHint(), 2 - 1); + return true; + } + }) + .build(); + + @SuppressWarnings("unused") + public GT_MetaTileEntity_MegaIndustrialApiary(int aID, String aName, String aNameRegional) { + super(aID, aName, aNameRegional); + } + + public GT_MetaTileEntity_MegaIndustrialApiary(String aName) { + super(aName); + } + + @Override + public void onRemoval() { + super.onRemoval(); + if (getBaseMetaTileEntity().isServerSide()) + tryOutputAll(mStorage, s -> Collections.singletonList(((BeeSimulator) s).queenStack)); + } + + private boolean isCacheDirty = true; + private final HashMap<String, String> flowersCache = new HashMap<>(); + private final HashSet<String> flowersCheck = new HashSet<>(); + private boolean flowersError = false; + private boolean needsTVarUpdate = false; + private int megaApiaryStorageVersion = 0; + + private void flowerCheck(final World world, final int x, final int y, final int z) { + if (!flowersCheck.isEmpty() && !world.isAirBlock(x, y, z)) + flowersCheck.removeIf(s -> FlowerManager.flowerRegistry.isAcceptedFlower(s, world, x, y, z)); + } + + @Override + public void construct(ItemStack stackSize, boolean hintsOnly) { + buildPiece(STRUCTURE_PIECE_MAIN, stackSize, hintsOnly, 7, 8, 0); + } + + @Override + public int survivalConstruct(ItemStack stackSize, int elementBudget, ISurvivalBuildEnvironment env) { + int built = survivialBuildPiece(STRUCTURE_PIECE_MAIN_SURVIVAL, stackSize, 7, 8, 0, elementBudget, env, true); + if (built == -1) { + GT_Utility.sendChatToPlayer( + env.getActor(), + EnumChatFormatting.GREEN + "Auto placing done ! Now go place the water and flowers yourself !"); + return 0; + } + return built; + } + + @Override + public IStructureDefinition<GT_MetaTileEntity_MegaIndustrialApiary> getStructureDefinition() { + return STRUCTURE_DEFINITION; + } + + @Override + protected IAlignmentLimits getInitialAlignmentLimits() { + return (d, r, f) -> d.offsetY == 0 && r.isNotRotated(); + } + + @Override + protected GT_Multiblock_Tooltip_Builder createTooltip() { + GT_Multiblock_Tooltip_Builder tt = new GT_Multiblock_Tooltip_Builder(); + tt.addMachineType("Mega Apiary") + .addInfo("Controller block for Industrial Apicultural Acclimatiser and Drone Domestication Station") + .addInfo(buildAuthorList("kuba6000", "Runakai")) + .addInfo("The ideal home for your bees") + .addInfo("Use screwdriver to change primary mode (INPUT/OUTPUT/OPERATING)") + .addInfo("Use screwdriver + shift to change operation mode (NORMAL/SWARMER)") + .addInfo("--------------------- INPUT MODE ---------------------") + .addInfo("- Does not take power") + .addInfo("- Put your queens in the input bus to put them in the internal buffer") + .addInfo("-------------------- OUTPUT MODE ---------------------") + .addInfo("- Does not take power") + .addInfo("- Will give your bees back to output bus") + .addInfo("------------------- OPERATING MODE -------------------") + .addInfo("- NORMAL:") + .addInfo(" - For each " + voltageTooltipFormatted(6) + " amp you can insert 1 bee") + .addInfo(" - Processing time: 5 seconds") + .addInfo(" - Uses 1 " + voltageTooltipFormatted(6) + " amp per queen") + .addInfo(" - All bees are accelerated 64 times") + .addInfo(" - 8 production upgrades are applied") + .addInfo(" - Genetic Stabilizer upgrade applied") + .addInfo(" - Simulates perfect environment for your bees") + .addInfo(" - Additionally you can provide royal jelly to increase the outputs:") + .addInfo(" - 1 royal jelly grants 5% bonus per bee") + .addInfo(" - They will be consumed on each start of operation") + .addInfo(" - and be applied to that operation only") + .addInfo(" - Max bonus: 200%") + .addInfo("- SWARMER:") + .addInfo(" - You can only insert 1 queen") + .addInfo(" - It will slowly produce ignoble princesses") + .addInfo(" - Consumes 100 royal jelly per operation") + .addInfo(" - Base processing time: 1 minute") + .addInfo(" - Uses 1 amp " + voltageTooltipFormatted(5)) + .addInfo(" - Can overclock") + .addInfo(StructureHologram) + .addSeparator() + .beginStructureBlock(15, 17, 15, false) + .addController("Front Bottom Center") + .addCasingInfoMin("Bronze Plated Bricks", 190, false) + .addOtherStructurePart("Borosilicate Glass", "Look at the hologram") + .addStructureInfo("The glass tier limits the Energy Input tier") + .addStructureInfo("Regular water and IC2 Distilled Water are accepted") + .addOtherStructurePart("Flowers", "On dirt/grass", 2) + .addInputBus("Any casing", 1) + .addOutputBus("Any casing", 1) + .addEnergyHatch("Any casing", 1) + .addMaintenanceHatch("Any casing", 1) + .toolTipFinisher(Tags.MODNAME); + return tt; + } + + @Override + public void saveNBTData(NBTTagCompound aNBT) { + super.saveNBTData(aNBT); + aNBT.setByte("mGlassTier", mGlassTier); + aNBT.setInteger("mPrimaryMode", mPrimaryMode); + aNBT.setInteger("mSecondaryMode", mSecondaryMode); + aNBT.setInteger("mStorageSize", mStorage.size()); + for (int i = 0; i < mStorage.size(); i++) aNBT.setTag( + "mStorage." + i, + mStorage.get(i) + .toNBTTagCompound()); + aNBT.setInteger("MEGA_APIARY_STORAGE_VERSION", MEGA_APIARY_STORAGE_VERSION); + } + + @Override + public void loadNBTData(NBTTagCompound aNBT) { + super.loadNBTData(aNBT); + mGlassTier = aNBT.getByte("mGlassTier"); + mPrimaryMode = aNBT.getInteger("mPrimaryMode"); + mSecondaryMode = aNBT.getInteger("mSecondaryMode"); + for (int i = 0, isize = aNBT.getInteger("mStorageSize"); i < isize; i++) + mStorage.add(new BeeSimulator(aNBT.getCompoundTag("mStorage." + i))); + megaApiaryStorageVersion = aNBT.getInteger("MEGA_APIARY_STORAGE_VERSION"); + flowersCache.clear(); + mStorage.forEach(s -> flowersCache.put(s.flowerType, s.flowerTypeDescription)); + flowersCache.remove(""); + isCacheDirty = false; + } + + @Override + public void onScrewdriverRightClick(ForgeDirection side, EntityPlayer aPlayer, float aX, float aY, float aZ) { + if (this.mMaxProgresstime > 0) { + GT_Utility.sendChatToPlayer(aPlayer, "Can't change mode when running !"); + return; + } + if (!aPlayer.isSneaking()) { + mPrimaryMode++; + if (mPrimaryMode == 3) mPrimaryMode = 0; + switch (mPrimaryMode) { + case 0: + GT_Utility.sendChatToPlayer(aPlayer, "Changed primary mode to: Input mode"); + break; + case 1: + GT_Utility.sendChatToPlayer(aPlayer, "Changed primary mode to: Output mode"); + break; + case 2: + GT_Utility.sendChatToPlayer(aPlayer, "Changed primary mode to: Operating mode"); + break; + } + } else { + if (!mStorage.isEmpty()) { + GT_Utility.sendChatToPlayer(aPlayer, "Can't change operating mode when the multi is not empty !"); + return; + } + mSecondaryMode++; + if (mSecondaryMode == 2) mSecondaryMode = 0; + switch (mSecondaryMode) { + case 0: + GT_Utility.sendChatToPlayer(aPlayer, "Changed secondary mode to: Normal mode"); + break; + case 1: + GT_Utility.sendChatToPlayer(aPlayer, "Changed secondary mode to: Swarmer mode"); + break; + } + } + } + + private void updateMaxSlots() { + int mOld = mMaxSlots; + long v = this.getMaxInputEu(); + if (v < GT_Values.V[6]) mMaxSlots = 0; + else if (mSecondaryMode == 0) mMaxSlots = (int) (v / GT_Values.V[6]); + else mMaxSlots = 1; + if (mOld != 0 && mOld != mMaxSlots) { + needsTVarUpdate = true; + } + } + + @Override + public void onPostTick(IGregTechTileEntity aBaseMetaTileEntity, long aTick) { + super.onPostTick(aBaseMetaTileEntity, aTick); + if (aBaseMetaTileEntity.isServerSide()) { + // TODO: Look for proper fix + if (mUpdate < 0) mUpdate = 600; + } else { + if (aBaseMetaTileEntity.isActive() && aTick % 100 == 0) { + int[] abc = new int[] { 0, -2, 7 }; + int[] xyz = new int[] { 0, 0, 0 }; + this.getExtendedFacing() + .getWorldOffset(abc, xyz); + xyz[0] += aBaseMetaTileEntity.getXCoord(); + xyz[1] += aBaseMetaTileEntity.getYCoord(); + xyz[2] += aBaseMetaTileEntity.getZCoord(); + showBees(aBaseMetaTileEntity.getWorld(), xyz[0], xyz[1], xyz[2], 100); + } + } + } + + @SideOnly(Side.CLIENT) + private void showBees(World world, int x, int y, int z, int age) { + MegaApiaryBeesRenderer bee = new MegaApiaryBeesRenderer(world, x, y, z, age); + Minecraft.getMinecraft().effectRenderer.addEffect(bee); + } + + @Override + @NotNull + public CheckRecipeResult checkProcessing() { + updateMaxSlots(); + if (mPrimaryMode < 2) { + if (mPrimaryMode == 0 && mStorage.size() < mMaxSlots) { + World w = getBaseMetaTileEntity().getWorld(); + float t = (float) getVoltageTierExact(); + ArrayList<ItemStack> inputs = getStoredInputs(); + for (ItemStack input : inputs) { + if (beeRoot.getType(input) == EnumBeeType.QUEEN) { + BeeSimulator bs = new BeeSimulator(input, w, t); + if (bs.isValid) { + mStorage.add(bs); + isCacheDirty = true; + } + } + if (mStorage.size() >= mMaxSlots) break; + } + updateSlots(); + } else if (mPrimaryMode == 1 && mStorage.size() > 0) { + if (tryOutputAll(mStorage, s -> Collections.singletonList(((BeeSimulator) s).queenStack))) + isCacheDirty = true; + } else return CheckRecipeResultRegistry.NO_RECIPE; + mMaxProgresstime = 10; + mEfficiency = (10000 - (getIdealStatus() - getRepairStatus()) * 1000); + mEfficiencyIncrease = 10000; + lEUt = 0; + return CheckRecipeResultRegistry.SUCCESSFUL; + } else if (mPrimaryMode == 2) { + if (mMaxSlots > 0 && !mStorage.isEmpty()) { + if (mSecondaryMode == 0) { + if (megaApiaryStorageVersion != MEGA_APIARY_STORAGE_VERSION) { + megaApiaryStorageVersion = MEGA_APIARY_STORAGE_VERSION; + World w = getBaseMetaTileEntity().getWorld(); + float t = (float) getVoltageTierExact(); + mStorage.forEach(s -> s.generate(w, t)); + } + + if (mStorage.size() > mMaxSlots) + return SimpleCheckRecipeResult.ofFailure("MegaApiary_slotoverflow"); + + if (flowersError) return SimpleCheckRecipeResult.ofFailure("MegaApiary_noflowers"); + + if (needsTVarUpdate) { + float t = (float) getVoltageTierExact(); + needsTVarUpdate = false; + World w = getBaseMetaTileEntity().getWorld(); + mStorage.forEach(s -> s.updateTVar(w, t)); + } + + int maxConsume = Math.min(mStorage.size(), mMaxSlots) * 40; + int toConsume = maxConsume; + ArrayList<ItemStack> inputs = getStoredInputs(); + + for (ItemStack input : inputs) { + if (!input.isItemEqual(royalJelly)) continue; + int consumed = Math.min(input.stackSize, toConsume); + toConsume -= consumed; + input.stackSize -= consumed; + if (toConsume == 0) break; + } + double boosted = 1d; + if (toConsume != maxConsume) { + boosted += (((double) maxConsume - (double) toConsume) / (double) maxConsume) * 2d; + this.updateSlots(); + } + + List<ItemStack> stacks = new ArrayList<>(); + for (int i = 0, mStorageSize = Math.min(mStorage.size(), mMaxSlots); i < mStorageSize; i++) { + BeeSimulator beeSimulator = mStorage.get(i); + stacks.addAll(beeSimulator.getDrops(this, 64_00d * boosted)); + } + + this.lEUt = -(int) ((double) GT_Values.V[6] * (double) mMaxSlots * 0.99d); + this.mEfficiency = (10000 - (getIdealStatus() - getRepairStatus()) * 1000); + this.mEfficiencyIncrease = 10000; + this.mMaxProgresstime = 100; + this.mOutputItems = stacks.toArray(new ItemStack[0]); + } else { + if (!depleteInput(PluginApiculture.items.royalJelly.getItemStack(64)) + || !depleteInput(PluginApiculture.items.royalJelly.getItemStack(36))) { + this.updateSlots(); + return CheckRecipeResultRegistry.NO_RECIPE; + } + calculateOverclock(GT_Values.V[5] - 2L, 1200); + if (this.lEUt > 0) this.lEUt = -this.lEUt; + this.mEfficiency = (10000 - (getIdealStatus() - getRepairStatus()) * 1000); + this.mEfficiencyIncrease = 10000; + this.mOutputItems = new ItemStack[] { this.mStorage.get(0) + .createIgnobleCopy() }; + this.updateSlots(); + } + return CheckRecipeResultRegistry.SUCCESSFUL; + } + } + + return CheckRecipeResultRegistry.NO_RECIPE; + } + + @Override + public String[] getInfoData() { + ArrayList<String> info = new ArrayList<>(Arrays.asList(super.getInfoData())); + info.add( + "Running in mode: " + EnumChatFormatting.GOLD + + (mPrimaryMode == 0 ? "Input mode" + : (mPrimaryMode == 1 ? "Output mode" + : (mSecondaryMode == 0 ? "Operating mode (NORMAL)" : "Operating mode (SWARMER)")))); + info.add( + "Bee storage (" + EnumChatFormatting.GOLD + + mStorage.size() + + EnumChatFormatting.RESET + + "/" + + (mStorage.size() > mMaxSlots ? EnumChatFormatting.DARK_RED.toString() + : EnumChatFormatting.GOLD.toString()) + + mMaxSlots + + EnumChatFormatting.RESET + + "):"); + HashMap<String, Integer> infos = new HashMap<>(); + for (int i = 0; i < mStorage.size(); i++) { + StringBuilder builder = new StringBuilder(); + if (i > mMaxSlots) builder.append(EnumChatFormatting.DARK_RED); + builder.append(EnumChatFormatting.GOLD); + builder.append(mStorage.get(i).queenStack.getDisplayName()); + infos.merge(builder.toString(), 1, Integer::sum); + } + infos.forEach((key, value) -> info.add("x" + value + ": " + key)); + + return info.toArray(new String[0]); + } + + @Override + public boolean checkMachine(IGregTechTileEntity aBaseMetaTileEntity, ItemStack aStack) { + mGlassTier = 0; + mCasing = 0; + if (isCacheDirty) { + flowersCache.clear(); + mStorage.forEach(s -> flowersCache.put(s.flowerType, s.flowerTypeDescription)); + flowersCache.remove(""); + isCacheDirty = false; + } + flowersCheck.clear(); + flowersCheck.addAll(flowersCache.keySet()); + if (!checkPiece(STRUCTURE_PIECE_MAIN, 7, 8, 0)) return false; + if (this.mGlassTier < 10 && !this.mEnergyHatches.isEmpty()) + for (GT_MetaTileEntity_Hatch_Energy hatchEnergy : this.mEnergyHatches) + if (this.mGlassTier < hatchEnergy.mTier) return false; + boolean valid = this.mMaintenanceHatches.size() == 1 && this.mEnergyHatches.size() >= 1 && this.mCasing >= 190; + flowersError = valid && !this.flowersCheck.isEmpty(); + if (valid) updateMaxSlots(); + return valid; + } + + @Override + public IMetaTileEntity newMetaEntity(IGregTechTileEntity aTileEntity) { + return new GT_MetaTileEntity_MegaIndustrialApiary(this.mName); + } + + @Override + public ITexture[] getTexture(IGregTechTileEntity aBaseMetaTileEntity, ForgeDirection side, ForgeDirection facing, + int colorIndex, boolean aActive, boolean aRedstone) { + if (side == facing) { + if (aActive) return new ITexture[] { Textures.BlockIcons.getCasingTextureForId(CASING_INDEX), + TextureFactory.builder() + .addIcon(OVERLAY_FRONT_DISTILLATION_TOWER_ACTIVE) + .extFacing() + .build(), + TextureFactory.builder() + .addIcon(OVERLAY_FRONT_DISTILLATION_TOWER_ACTIVE_GLOW) + .extFacing() + .glow() + .build() }; + return new ITexture[] { Textures.BlockIcons.getCasingTextureForId(CASING_INDEX), TextureFactory.builder() + .addIcon(OVERLAY_FRONT_DISTILLATION_TOWER) + .extFacing() + .build(), + TextureFactory.builder() + .addIcon(OVERLAY_FRONT_DISTILLATION_TOWER_GLOW) + .extFacing() + .glow() + .build() }; + } + return new ITexture[] { Textures.BlockIcons.getCasingTextureForId(CASING_INDEX) }; + } + + private static final UIInfo<?, ?> MegaApiaryUI = createKTMetaTileEntityUI( + KT_ModulaUIContainer_MegaIndustrialApiary::new); + + @Override + public boolean onRightclick(IGregTechTileEntity aBaseMetaTileEntity, EntityPlayer aPlayer) { + if (aBaseMetaTileEntity.isClientSide()) return true; + MegaApiaryUI.open( + aPlayer, + aBaseMetaTileEntity.getWorld(), + aBaseMetaTileEntity.getXCoord(), + aBaseMetaTileEntity.getYCoord(), + aBaseMetaTileEntity.getZCoord()); + return true; + } + + private static class KT_ModulaUIContainer_MegaIndustrialApiary extends ModularUIContainer { + + final WeakReference<GT_MetaTileEntity_MegaIndustrialApiary> parent; + + public KT_ModulaUIContainer_MegaIndustrialApiary(ModularUIContext context, ModularWindow mainWindow, + GT_MetaTileEntity_MegaIndustrialApiary mte) { + super(context, mainWindow); + parent = new WeakReference<>(mte); + } + + @Override + public ItemStack transferStackInSlot(EntityPlayer aPlayer, int aSlotIndex) { + if (!(aPlayer instanceof EntityPlayerMP)) return super.transferStackInSlot(aPlayer, aSlotIndex); + final Slot s = getSlot(aSlotIndex); + if (s == null) return super.transferStackInSlot(aPlayer, aSlotIndex); + if (aSlotIndex >= 36) return super.transferStackInSlot(aPlayer, aSlotIndex); + final ItemStack aStack = s.getStack(); + if (aStack == null) return super.transferStackInSlot(aPlayer, aSlotIndex); + GT_MetaTileEntity_MegaIndustrialApiary mte = parent.get(); + if (mte == null) return super.transferStackInSlot(aPlayer, aSlotIndex); + if (mte.mStorage.size() >= mte.mMaxSlots) return super.transferStackInSlot(aPlayer, aSlotIndex); + if (beeRoot.getType(aStack) == EnumBeeType.QUEEN) { + if (mte.mMaxProgresstime > 0) { + GT_Utility.sendChatToPlayer(aPlayer, EnumChatFormatting.RED + "Can't insert while running !"); + return super.transferStackInSlot(aPlayer, aSlotIndex); + } + World w = mte.getBaseMetaTileEntity() + .getWorld(); + float t = (float) mte.getVoltageTierExact(); + BeeSimulator bs = new BeeSimulator(aStack, w, t); + if (bs.isValid) { + mte.mStorage.add(bs); + s.putStack(null); + detectAndSendChanges(); + mte.isCacheDirty = true; + return null; + } + } + return super.transferStackInSlot(aPlayer, aSlotIndex); + } + } + + DynamicInventory<BeeSimulator> dynamicInventory = new DynamicInventory<>( + 128, + 60, + () -> mMaxSlots, + mStorage, + s -> s.queenStack).allowInventoryInjection(input -> { + World w = getBaseMetaTileEntity().getWorld(); + float t = (float) getVoltageTierExact(); + BeeSimulator bs = new BeeSimulator(input, w, t); + if (bs.isValid) { + mStorage.add(bs); + return input; + } + return null; + }) + .allowInventoryExtraction(mStorage::remove) + .allowInventoryReplace((i, stack) -> { + if (stack.stackSize != 1) return null; + World w = getBaseMetaTileEntity().getWorld(); + float t = (float) getVoltageTierExact(); + BeeSimulator bs = new BeeSimulator(stack, w, t); + if (bs.isValid) { + BeeSimulator removed = mStorage.remove(i); + mStorage.add(i, bs); + return removed.queenStack; + } + return null; + }) + .setEnabled(() -> this.mMaxProgresstime == 0); + + @Override + public void createInventorySlots() { + + } + + private boolean isInInventory = true; + + @Override + public void addUIWidgets(ModularWindow.Builder builder, UIBuildContext buildContext) { + isInInventory = !getBaseMetaTileEntity().isActive(); + builder.widget( + new DrawableWidget().setDrawable(GT_UITextures.PICTURE_SCREEN_BLACK) + .setPos(4, 4) + .setSize(190, 85) + .setEnabled(w -> !isInInventory)); + 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")) + .setBackground(GT_UITextures.BUTTON_STANDARD) + .setPos(140, 4) + .setSize(55, 16)); + + final DynamicPositionedColumn screenElements = new DynamicPositionedColumn(); + drawTexts(screenElements, null); + builder.widget(screenElements.setEnabled(w -> !isInInventory)); + + builder.widget(createPowerSwitchButton(builder)) + .widget(createVoidExcessButton(builder)) + .widget(createInputSeparationButton(builder)) + .widget(createBatchModeButton(builder)) + .widget(createLockToSingleRecipeButton(builder)); + + DynamicPositionedRow configurationElements = new DynamicPositionedRow(); + addConfigurationWidgets(configurationElements, buildContext); + + builder.widget( + configurationElements.setSpace(2) + .setAlignment(MainAxisAlignment.SPACE_BETWEEN) + .setPos(getRecipeLockingButtonPos().add(18, 0))); + } + + @Override + protected void addConfigurationWidgets(DynamicPositionedRow configurationElements, UIBuildContext buildContext) { + buildContext.addSyncedWindow(CONFIGURATION_WINDOW_ID, this::createConfigurationWindow); + configurationElements.setSynced(false); + configurationElements.widget( + new ButtonWidget().setOnClick( + (clickData, widget) -> { + if (!widget.isClient()) widget.getContext() + .openSyncedWindow(CONFIGURATION_WINDOW_ID); + }) + .setBackground(GT_UITextures.BUTTON_STANDARD, GT_UITextures.OVERLAY_BUTTON_CYCLIC) + .addTooltip("Configuration") + .setSize(16, 16)); + } + + protected ModularWindow createConfigurationWindow(final EntityPlayer player) { + ModularWindow.Builder builder = ModularWindow.builder(200, 100); + builder.setBackground(ModularUITextures.VANILLA_BACKGROUND); + builder.widget( + new DrawableWidget().setDrawable(GT_UITextures.OVERLAY_BUTTON_CYCLIC) + .setPos(5, 5) + .setSize(16, 16)) + .widget(new TextWidget("Configuration").setPos(25, 9)) + .widget( + ButtonWidget.closeWindowButton(true) + .setPos(185, 3)) + .widget( + new Column().widget( + new CycleButtonWidget().setLength(3) + .setGetter(() -> mPrimaryMode) + .setSetter(val -> { + if (this.mMaxProgresstime > 0) { + GT_Utility.sendChatToPlayer(player, "Can't change mode when running !"); + return; + } + mPrimaryMode = val; + + if (!(player instanceof EntityPlayerMP)) return; + switch (mPrimaryMode) { + case 0: + GT_Utility.sendChatToPlayer(player, "Changed primary mode to: Input mode"); + break; + case 1: + GT_Utility.sendChatToPlayer(player, "Changed primary mode to: Output mode"); + break; + case 2: + GT_Utility.sendChatToPlayer(player, "Changed primary mode to: Operating mode"); + break; + } + }) + .addTooltip(0, new Text("Input").color(Color.YELLOW.dark(3))) + .addTooltip(1, new Text("Output").color(Color.YELLOW.dark(3))) + .addTooltip(2, new Text("Operating").color(Color.GREEN.dark(3))) + .setTextureGetter( + i -> i == 0 ? new Text("Input").color(Color.YELLOW.dark(3)) + .withFixedSize(70 - 18, 18, 15, 0) + : i == 1 ? new Text("Output").color(Color.YELLOW.dark(3)) + .withFixedSize(70 - 18, 18, 15, 0) + : new Text("Operating").color(Color.GREEN.dark(3)) + .withFixedSize(70 - 18, 18, 15, 0)) + .setBackground( + ModularUITextures.VANILLA_BACKGROUND, + GT_UITextures.OVERLAY_BUTTON_CYCLIC.withFixedSize(18, 18)) + .setSize(70, 18) + .addTooltip("Primary mode")) + .widget( + new CycleButtonWidget().setLength(2) + .setGetter(() -> mSecondaryMode) + .setSetter(val -> { + if (this.mMaxProgresstime > 0) { + GT_Utility.sendChatToPlayer(player, "Can't change mode when running !"); + return; + } + + mSecondaryMode = val; + + if (!(player instanceof EntityPlayerMP)) return; + switch (mSecondaryMode) { + case 0: + GT_Utility.sendChatToPlayer(player, "Changed secondary mode to: Normal mode"); + break; + case 1: + GT_Utility.sendChatToPlayer(player, "Changed secondary mode to: Swarmer mode"); + break; + } + }) + .addTooltip(0, new Text("Normal").color(Color.GREEN.dark(3))) + .addTooltip(1, new Text("Swarmer").color(Color.YELLOW.dark(3))) + .setTextureGetter( + i -> i == 0 ? new Text("Normal").color(Color.GREEN.dark(3)) + .withFixedSize(70 - 18, 18, 15, 0) + : new Text("Swarmer").color(Color.YELLOW.dark(3)) + .withFixedSize(70 - 18, 18, 15, 0)) + .setBackground( + ModularUITextures.VANILLA_BACKGROUND, + GT_UITextures.OVERLAY_BUTTON_CYCLIC.withFixedSize(18, 18)) + .setSize(70, 18) + .addTooltip("Secondary mode")) + .setEnabled(widget -> !getBaseMetaTileEntity().isActive()) + .setPos(10, 30)) + .widget( + new Column().widget(new TextWidget("Primary mode").setSize(100, 18)) + .widget(new TextWidget("Secondary mode").setSize(100, 18)) + .setEnabled(widget -> !getBaseMetaTileEntity().isActive()) + .setPos(80, 30)) + .widget( + new DrawableWidget().setDrawable(GT_UITextures.OVERLAY_BUTTON_CROSS) + .setSize(18, 18) + .setPos(10, 30) + .addTooltip(new Text("Can't change configuration when running !").color(Color.RED.dark(3))) + .setEnabled(widget -> getBaseMetaTileEntity().isActive())); + return builder.build(); + } + + // private List<String> flowersGUI = Collections.emptyList(); + + private HashMap<ItemStack, Double> GUIDropProgress = new HashMap<>(); + + @Override + protected String generateCurrentRecipeInfoString() { + if (mSecondaryMode == 1) return super.generateCurrentRecipeInfoString(); + StringBuilder ret = new StringBuilder(EnumChatFormatting.WHITE + "Progress: ") + .append(String.format("%,.2f", (double) mProgresstime / 20)) + .append("s / ") + .append(String.format("%,.2f", (double) mMaxProgresstime / 20)) + .append("s (") + .append(String.format("%,.1f", (double) mProgresstime / mMaxProgresstime * 100)) + .append("%)\n"); + + for (Map.Entry<ItemStack, Double> drop : GUIDropProgress.entrySet()) { + int outputSize = Arrays.stream(mOutputItems) + .filter(s -> s.isItemEqual(drop.getKey())) + .mapToInt(i -> i.stackSize) + .sum(); + ret.append(EnumChatFormatting.AQUA) + .append( + drop.getKey() + .getDisplayName()) + .append(EnumChatFormatting.WHITE) + .append(": "); + if (outputSize == 0) { + ret.append(String.format("%.2f", drop.getValue() * 100)) + .append("%\n"); + } else { + ret.append(EnumChatFormatting.GOLD) + .append( + String.format( + "x%d %s(+%.2f/sec)\n", + outputSize, + EnumChatFormatting.WHITE, + (double) outputSize / (mMaxProgresstime / 20))); + } + } + + return ret.toString(); + } + + @Override + protected void drawTexts(DynamicPositionedColumn screenElements, SlotWidget inventorySlot) { + + screenElements.widget(new FakeSyncWidget.IntegerSyncer(() -> mSecondaryMode, b -> mSecondaryMode = b)); + screenElements.widget(new FakeSyncWidget<>(() -> { + HashMap<ItemStack, Double> ret = new HashMap<>(); + HashMap<ItemID, Double> dropProgress = new HashMap<>(); + + for (Map.Entry<ItemID, Double> drop : this.dropProgress.entrySet()) { + dropProgress.merge(drop.getKey(), drop.getValue(), Double::sum); + } + + for (Map.Entry<ItemID, Double> drop : dropProgress.entrySet()) { + ret.put(BeeSimulator.dropstacks.get(drop.getKey()), drop.getValue()); + } + return ret; + }, h -> GUIDropProgress = h, (buffer, h) -> { + buffer.writeVarIntToBuffer(h.size()); + for (Map.Entry<ItemStack, Double> itemStackDoubleEntry : h.entrySet()) { + try { + buffer.writeItemStackToBuffer(itemStackDoubleEntry.getKey()); + buffer.writeDouble(itemStackDoubleEntry.getValue()); + } catch (IOException e) { + throw new RuntimeException(e); + } + } + }, buffer -> { + int len = buffer.readVarIntFromBuffer(); + HashMap<ItemStack, Double> ret = new HashMap<>(len); + for (int i = 0; i < len; i++) { + try { + ret.put(buffer.readItemStackFromBuffer(), buffer.readDouble()); + } catch (IOException e) { + throw new RuntimeException(e); + } + } + return ret; + })); + super.drawTexts(screenElements, inventorySlot); + } + + final HashMap<ItemID, Double> dropProgress = new HashMap<>(); + + private static class BeeSimulator { + + final ItemStack queenStack; + boolean isValid; + List<BeeDrop> drops = new ArrayList<>(); + List<BeeDrop> specialDrops = new ArrayList<>(); + float beeSpeed; + + float maxBeeCycles; + String flowerType; + String flowerTypeDescription; + private static IBeekeepingMode mode; + + public BeeSimulator(ItemStack queenStack, World world, float t) { + isValid = false; + this.queenStack = queenStack.copy(); + this.queenStack.stackSize = 1; + generate(world, t); + isValid = true; + queenStack.stackSize--; + } + + public void generate(World world, float t) { + if (mode == null) mode = beeRoot.getBeekeepingMode(world); + drops.clear(); + specialDrops.clear(); + if (beeRoot.getType(this.queenStack) != EnumBeeType.QUEEN) return; + IBee queen = beeRoot.getMember(this.queenStack); + IBeeModifier beeModifier = mode.getBeeModifier(); + float mod = beeModifier.getLifespanModifier(null, null, 1.f); + int h = queen.getMaxHealth(); + maxBeeCycles = (float) h / (1.f / mod); + IBeeGenome genome = queen.getGenome(); + this.flowerType = genome.getFlowerProvider() + .getFlowerType(); + this.flowerTypeDescription = genome.getFlowerProvider() + .getDescription(); + IAlleleBeeSpecies primary = genome.getPrimary(); + beeSpeed = genome.getSpeed(); + genome.getPrimary() + .getProductChances() + .forEach((key, value) -> drops.add(new BeeDrop(key, value, beeSpeed, t))); + genome.getSecondary() + .getProductChances() + .forEach((key, value) -> drops.add(new BeeDrop(key, value / 2.f, beeSpeed, t))); + primary.getSpecialtyChances() + .forEach((key, value) -> specialDrops.add(new BeeDrop(key, value, beeSpeed, t))); + } + + public BeeSimulator(NBTTagCompound tag) { + queenStack = readItemStackFromNBT(tag.getCompoundTag("queenStack")); + isValid = tag.getBoolean("isValid"); + drops = new ArrayList<>(); + specialDrops = new ArrayList<>(); + for (int i = 0, isize = tag.getInteger("dropssize"); i < isize; i++) + drops.add(new BeeDrop(tag.getCompoundTag("drops" + i))); + for (int i = 0, isize = tag.getInteger("specialDropssize"); i < isize; i++) + specialDrops.add(new BeeDrop(tag.getCompoundTag("specialDrops" + i))); + beeSpeed = tag.getFloat("beeSpeed"); + maxBeeCycles = tag.getFloat("maxBeeCycles"); + if (tag.hasKey("flowerType") && tag.hasKey("flowerTypeDescription")) { + flowerType = tag.getString("flowerType"); + flowerTypeDescription = tag.getString("flowerTypeDescription"); + } else { + IBee queen = beeRoot.getMember(this.queenStack); + IBeeGenome genome = queen.getGenome(); + this.flowerType = genome.getFlowerProvider() + .getFlowerType(); + this.flowerTypeDescription = genome.getFlowerProvider() + .getDescription(); + } + } + + public NBTTagCompound toNBTTagCompound() { + NBTTagCompound tag = new NBTTagCompound(); + tag.setTag("queenStack", writeItemStackToNBT(queenStack)); + tag.setBoolean("isValid", isValid); + tag.setInteger("dropssize", drops.size()); + for (int i = 0; i < drops.size(); i++) tag.setTag( + "drops" + i, + drops.get(i) + .toNBTTagCompound()); + tag.setInteger("specialDropssize", specialDrops.size()); + for (int i = 0; i < specialDrops.size(); i++) tag.setTag( + "specialDrops" + i, + specialDrops.get(i) + .toNBTTagCompound()); + tag.setFloat("beeSpeed", beeSpeed); + tag.setFloat("maxBeeCycles", maxBeeCycles); + tag.setString("flowerType", flowerType); + tag.setString("flowerTypeDescription", flowerTypeDescription); + return tag; + } + + static final Map<ItemID, ItemStack> dropstacks = new HashMap<>(); + + public List<ItemStack> getDrops(final GT_MetaTileEntity_MegaIndustrialApiary MTE, final double timePassed) { + drops.forEach(d -> { + MTE.dropProgress.merge(d.id, d.getAmount(timePassed / 550d), Double::sum); + if (!dropstacks.containsKey(d.id)) dropstacks.put(d.id, d.stack); + }); + specialDrops.forEach(d -> { + MTE.dropProgress.merge(d.id, d.getAmount(timePassed / 550d), Double::sum); + if (!dropstacks.containsKey(d.id)) dropstacks.put(d.id, d.stack); + }); + List<ItemStack> currentDrops = new ArrayList<>(); + MTE.dropProgress.entrySet() + .forEach(e -> { + double v = e.getValue(); + while (v > 1.f) { + int size = Math.min((int) v, 64); + ItemStack stack = dropstacks.get(e.getKey()) + .copy(); + stack.stackSize = size; + currentDrops.add(stack); + v -= size; + e.setValue(v); + } + }); + return currentDrops; + } + + public ItemStack createIgnobleCopy() { + IBee princess = beeRoot.getMember(queenStack); + princess.setIsNatural(false); + return beeRoot.getMemberStack(princess, EnumBeeType.PRINCESS.ordinal()); + } + + public void updateTVar(World world, float t) { + if (mode == null) mode = beeRoot.getBeekeepingMode(world); + drops.forEach(d -> d.updateTVar(t)); + specialDrops.forEach(d -> d.updateTVar(t)); + } + + private static class BeeDrop { + + private static final float MAX_PRODUCTION_MODIFIER_FROM_UPGRADES = 17.19926784f; // 4*1.2^8 + final ItemStack stack; + double amount; + final ItemID id; + + final float chance; + final float beeSpeed; + float t; + + public BeeDrop(ItemStack stack, float chance, float beeSpeed, float t) { + this.stack = stack; + this.chance = chance; + this.beeSpeed = beeSpeed; + this.t = t; + id = ItemID.createNoCopy(this.stack); + evaluate(); + } + + public void updateTVar(float t) { + if (this.t != t) { + this.t = t; + evaluate(); + } + } + + public void evaluate() { + this.amount = Bee.getFinalChance( + chance, + beeSpeed, + MAX_PRODUCTION_MODIFIER_FROM_UPGRADES + mode.getBeeModifier() + .getProductionModifier(null, MAX_PRODUCTION_MODIFIER_FROM_UPGRADES), + t); + } + + public double getAmount(double speedModifier) { + return amount * speedModifier; + } + + public ItemStack get(int amount) { + ItemStack r = stack.copy(); + r.stackSize = amount; + return r; + } + + public BeeDrop(NBTTagCompound tag) { + stack = readItemStackFromNBT(tag.getCompoundTag("stack")); + chance = tag.getFloat("chance"); + beeSpeed = tag.getFloat("beeSpeed"); + t = tag.getFloat("t"); + amount = tag.getDouble("amount"); + id = ItemID.createNoCopy(stack); + } + + public NBTTagCompound toNBTTagCompound() { + NBTTagCompound tag = new NBTTagCompound(); + tag.setTag("stack", writeItemStackToNBT(stack)); + tag.setFloat("chance", chance); + tag.setFloat("beeSpeed", beeSpeed); + tag.setFloat("t", t); + tag.setDouble("amount", amount); + return tag; + } + + @Override + public int hashCode() { + return id.hashCode(); + } + } + } + +} |