From 1b820de08a05070909a267e17f033fcf58ac8710 Mon Sep 17 00:00:00 2001 From: NotAPenguin Date: Mon, 2 Sep 2024 23:17:17 +0200 Subject: The Great Renaming (#3014) * move kekztech to a single root dir * move detrav to a single root dir * move gtnh-lanthanides to a single root dir * move tectech and delete some gross reflection in gt++ * remove more reflection inside gt5u * delete more reflection in gt++ * fix imports * move bartworks and bwcrossmod * fix proxies * move galactigreg and ggfab * move gtneioreplugin * try to fix gt++ bee loader * apply the rename rules to BW * apply rename rules to bwcrossmod * apply rename rules to detrav scanner mod * apply rename rules to galacticgreg * apply rename rules to ggfab * apply rename rules to goodgenerator * apply rename rules to gtnh-lanthanides * apply rename rules to gt++ * apply rename rules to kekztech * apply rename rules to kubatech * apply rename rules to tectech * apply rename rules to gt apply the rename rules to gt * fix tt import * fix mui hopefully * fix coremod except intergalactic * rename assline recipe class * fix a class name i stumbled on * rename StructureUtility to GTStructureUtility to prevent conflict with structurelib * temporary rename of GTTooltipDataCache to old name * fix gt client/server proxy names --- .../kekztech/common/tileentities/MTEHatchTFFT.java | 271 +++++ .../tileentities/MTELapotronicSuperCapacitor.java | 1213 ++++++++++++++++++++ .../common/tileentities/MTESOFuelCellMK1.java | 221 ++++ .../common/tileentities/MTESOFuelCellMK2.java | 222 ++++ .../kekztech/common/tileentities/MTETankTFFT.java | 787 +++++++++++++ .../common/tileentities/TileEntityIchorJar.java | 10 + .../tileentities/TileEntityIchorVoidJar.java | 10 + .../TileEntityThaumiumReinforcedJar.java | 10 + .../TileEntityThaumiumReinforcedVoidJar.java | 10 + 9 files changed, 2754 insertions(+) create mode 100644 src/main/java/kekztech/common/tileentities/MTEHatchTFFT.java create mode 100644 src/main/java/kekztech/common/tileentities/MTELapotronicSuperCapacitor.java create mode 100644 src/main/java/kekztech/common/tileentities/MTESOFuelCellMK1.java create mode 100644 src/main/java/kekztech/common/tileentities/MTESOFuelCellMK2.java create mode 100644 src/main/java/kekztech/common/tileentities/MTETankTFFT.java create mode 100644 src/main/java/kekztech/common/tileentities/TileEntityIchorJar.java create mode 100644 src/main/java/kekztech/common/tileentities/TileEntityIchorVoidJar.java create mode 100644 src/main/java/kekztech/common/tileentities/TileEntityThaumiumReinforcedJar.java create mode 100644 src/main/java/kekztech/common/tileentities/TileEntityThaumiumReinforcedVoidJar.java (limited to 'src/main/java/kekztech/common/tileentities') diff --git a/src/main/java/kekztech/common/tileentities/MTEHatchTFFT.java b/src/main/java/kekztech/common/tileentities/MTEHatchTFFT.java new file mode 100644 index 0000000000..66e93a7cad --- /dev/null +++ b/src/main/java/kekztech/common/tileentities/MTEHatchTFFT.java @@ -0,0 +1,271 @@ +package kekztech.common.tileentities; + +import java.util.HashMap; + +import net.minecraft.tileentity.TileEntity; +import net.minecraftforge.common.util.ForgeDirection; +import net.minecraftforge.fluids.FluidStack; +import net.minecraftforge.fluids.FluidTankInfo; + +import appeng.api.AEApi; +import appeng.api.config.AccessRestriction; +import appeng.api.config.Actionable; +import appeng.api.networking.security.BaseActionSource; +import appeng.api.storage.IExternalStorageHandler; +import appeng.api.storage.IMEInventory; +import appeng.api.storage.IMEMonitor; +import appeng.api.storage.IMEMonitorHandlerReceiver; +import appeng.api.storage.StorageChannel; +import appeng.api.storage.data.IAEFluidStack; +import appeng.api.storage.data.IItemList; +import appeng.util.item.AEFluidStack; +import appeng.util.item.FluidList; +import cpw.mods.fml.common.Optional; +import gregtech.api.enums.Textures; +import gregtech.api.fluid.GTFluidTank; +import gregtech.api.interfaces.ITexture; +import gregtech.api.interfaces.metatileentity.IMetaTileEntity; +import gregtech.api.interfaces.tileentity.IGregTechTileEntity; +import gregtech.api.metatileentity.BaseMetaTileEntity; +import gregtech.api.metatileentity.implementations.MTEHatch; +import gregtech.api.render.TextureFactory; + +@Optional.Interface(iface = "appeng.api.storage.IMEMonitor", modid = "appliedenergistics2", striprefs = true) +public class MTEHatchTFFT extends MTEHatch implements IMEMonitor { + + @Optional.Interface( + iface = "appeng.api.storage.IExternalStorageHandler", + modid = "appliedenergistics2", + striprefs = true) + private static class AE2TFFTHatchHandler implements IExternalStorageHandler { + + @Override + @Optional.Method(modid = "appliedenergistics2") + public boolean canHandle(TileEntity te, ForgeDirection d, StorageChannel channel, BaseActionSource mySrc) { + return channel == StorageChannel.FLUIDS && te instanceof BaseMetaTileEntity + && ((BaseMetaTileEntity) te).getMetaTileEntity() instanceof MTEHatchTFFT; + } + + @Override + @Optional.Method(modid = "appliedenergistics2") + public IMEInventory getInventory(TileEntity te, ForgeDirection d, StorageChannel channel, + BaseActionSource src) { + if (channel == StorageChannel.FLUIDS) { + return ((MTEHatchTFFT) (((BaseMetaTileEntity) te).getMetaTileEntity())); + } + return null; + } + } + + private static final Textures.BlockIcons.CustomIcon TEXTURE_TFFT_HATCH = new Textures.BlockIcons.CustomIcon( + "iconsets/TFFT_HATCH"); + + private HashMap, Object> listeners = new HashMap<>(); + private MTETankTFFT controller; + + public MTEHatchTFFT(int aID, String aName, String aNameRegional) { + super(aID, aName, aNameRegional, 3, 0, "All-in-one access for the T.F.F.T"); + } + + public MTEHatchTFFT(String aName, int aTier, String[] aDescription, ITexture[][][] aTextures) { + super(aName, aTier, 0, aDescription, aTextures); + } + + @Override + public boolean isFacingValid(ForgeDirection facing) { + return true; + } + + @Override + public ITexture[] getTexture(IGregTechTileEntity aBaseMetaTileEntity, ForgeDirection side, ForgeDirection facing, + int colorIndex, boolean aActive, boolean aRedstone) { + return super.getTexture(aBaseMetaTileEntity, side, facing, colorIndex, aActive, aRedstone); + } + + @Override + public ITexture[] getTexturesActive(ITexture aBaseTexture) { + return new ITexture[] { aBaseTexture, TextureFactory.of(Textures.BlockIcons.OVERLAY_PIPE_IN), + TextureFactory.builder() + .addIcon(TEXTURE_TFFT_HATCH) + .extFacing() + .build() }; + } + + @Override + public ITexture[] getTexturesInactive(ITexture aBaseTexture) { + return new ITexture[] { aBaseTexture, TextureFactory.of(Textures.BlockIcons.OVERLAY_PIPE_IN), + TextureFactory.builder() + .addIcon(TEXTURE_TFFT_HATCH) + .extFacing() + .build() }; + } + + @Override + public IMetaTileEntity newMetaEntity(IGregTechTileEntity aTileEntity) { + return new MTEHatchTFFT(mName, mTier, mDescriptionArray, mTextures); + } + + @Override + public int fill(ForgeDirection from, FluidStack resource, boolean doFill) { + return (controller != null) ? controller.pull(resource, doFill) : 0; + } + + @Override + public FluidStack drain(ForgeDirection from, FluidStack resource, boolean doDrain) { + if (controller != null) { + final GTFluidTank sFluid = controller.getSelectedFluid(); + if (controller.getFluidSelector() == -1 || (sFluid != null && sFluid.contains(resource))) { + return controller.push(resource, doDrain); + } + } + return null; + } + + @Override + public FluidStack drain(ForgeDirection from, int maxDrain, boolean doDrain) { + if (controller != null) { + final GTFluidTank sFluid = controller.getSelectedFluid(); + if (controller.getFluidSelector() == -1) return controller.push(maxDrain, doDrain); + if (sFluid != null) return controller.push(sFluid.get(maxDrain), doDrain); + } + return null; + } + + @Override + public FluidTankInfo[] getTankInfo(ForgeDirection from) { + return (controller != null) ? controller.getTankInfo() : null; + } + + public void bind(MTETankTFFT controller) { + this.controller = controller; + } + + public void unbind() { + this.controller = null; + } + + @Optional.Method(modid = "appliedenergistics2") + public static void registerAEIntegration() { + AEApi.instance() + .registries() + .externalStorage() + .addExternalStorageInterface(new AE2TFFTHatchHandler()); + } + + @Override + @Optional.Method(modid = "appliedenergistics2") + public IItemList getAvailableItems(IItemList out, int iteration) { + if (controller != null) { + for (int i = 0; i < MTETankTFFT.MAX_DISTINCT_FLUIDS; i++) { + if (!controller.STORE[i].isEmpty()) { + IAEFluidStack s = AEFluidStack.create(controller.STORE[i].get()); + s.setStackSize(controller.STORE[i].amount()); + out.add(s); + } + } + } + return out; + } + + @Override + @Optional.Method(modid = "appliedenergistics2") + public IItemList getStorageList() { + IItemList fluidList = new FluidList(); + if (controller != null) { + for (int i = 0; i < MTETankTFFT.MAX_DISTINCT_FLUIDS; i++) { + if (!controller.STORE[i].isEmpty()) { + IAEFluidStack s = AEFluidStack.create(controller.STORE[i].get()); + s.setStackSize(controller.STORE[i].amount()); + fluidList.add(s); + } + } + } + return fluidList; + } + + @Override + @Optional.Method(modid = "appliedenergistics2") + public void addListener(IMEMonitorHandlerReceiver l, Object verificationToken) { + if (listeners == null) listeners = new HashMap<>(); + listeners.put(l, verificationToken); + } + + @Override + @Optional.Method(modid = "appliedenergistics2") + public void removeListener(IMEMonitorHandlerReceiver l) { + if (listeners == null) listeners = new HashMap<>(); + listeners.remove(l); + } + + @Override + @Optional.Method(modid = "appliedenergistics2") + public AccessRestriction getAccess() { + return AccessRestriction.READ_WRITE; + } + + @Override + @Optional.Method(modid = "appliedenergistics2") + public boolean isPrioritized(IAEFluidStack input) { + if (controller == null || input == null) return false; + return controller.contains(input.getFluidStack()) || controller.fluidCount() < MTETankTFFT.MAX_DISTINCT_FLUIDS; + } + + @Override + @Optional.Method(modid = "appliedenergistics2") + public boolean canAccept(IAEFluidStack input) { + if (controller == null || input == null) return false; + return controller.contains(input.getFluidStack()) || controller.fluidCount() < MTETankTFFT.MAX_DISTINCT_FLUIDS; + } + + @Override + @Optional.Method(modid = "appliedenergistics2") + public int getPriority() { + return 0; + } + + @Override + @Optional.Method(modid = "appliedenergistics2") + public int getSlot() { + return 0; + } + + @Override + @Optional.Method(modid = "appliedenergistics2") + public boolean validForPass(int i) { + return true; + } + + @Override + @Optional.Method(modid = "appliedenergistics2") + public IAEFluidStack injectItems(IAEFluidStack input, Actionable mode, BaseActionSource src) { + final FluidStack inputStack = input.getFluidStack(); + if (inputStack == null) return null; + if (controller == null || getBaseMetaTileEntity() == null) return input; + if (mode != Actionable.SIMULATE) getBaseMetaTileEntity().markDirty(); + long amount = controller.pull(input.getFluidStack(), input.getStackSize(), mode != Actionable.SIMULATE); + if (amount == 0) return input; + if (amount == input.getStackSize()) return null; + IAEFluidStack result = AEFluidStack.create(input.getFluidStack()); + result.setStackSize(input.getStackSize() - amount); + return result; + } + + @Override + @Optional.Method(modid = "appliedenergistics2") + public IAEFluidStack extractItems(IAEFluidStack request, Actionable mode, BaseActionSource src) { + if (controller == null || getBaseMetaTileEntity() == null) return null; + if (mode != Actionable.SIMULATE) getBaseMetaTileEntity().markDirty(); + long amount = controller.push(request.getFluidStack(), request.getStackSize(), mode != Actionable.SIMULATE); + if (amount == 0) return null; + if (amount == request.getStackSize()) return request.copy(); + IAEFluidStack result = AEFluidStack.create(request.getFluidStack()); + result.setStackSize(amount); + return result; + } + + @Override + @Optional.Method(modid = "appliedenergistics2") + public StorageChannel getChannel() { + return StorageChannel.FLUIDS; + } +} diff --git a/src/main/java/kekztech/common/tileentities/MTELapotronicSuperCapacitor.java b/src/main/java/kekztech/common/tileentities/MTELapotronicSuperCapacitor.java new file mode 100644 index 0000000000..2672d0e994 --- /dev/null +++ b/src/main/java/kekztech/common/tileentities/MTELapotronicSuperCapacitor.java @@ -0,0 +1,1213 @@ +package kekztech.common.tileentities; + +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.onlyIf; +import static com.gtnewhorizon.structurelib.structure.StructureUtility.transpose; +import static com.gtnewhorizon.structurelib.structure.StructureUtility.withChannel; +import static gregtech.api.enums.HatchElement.Maintenance; +import static gregtech.api.metatileentity.BaseTileEntity.TOOLTIP_DELAY; +import static gregtech.api.util.GTStructureUtility.buildHatchAdder; +import static gregtech.api.util.GTStructureUtility.filterByMTEClass; +import static java.lang.Math.min; +import static kekztech.util.Util.toPercentageFrom; +import static kekztech.util.Util.toStandardForm; + +import java.math.BigInteger; +import java.text.NumberFormat; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Comparator; +import java.util.HashSet; +import java.util.LinkedList; +import java.util.List; +import java.util.Queue; +import java.util.Set; +import java.util.UUID; +import java.util.function.Consumer; + +import net.minecraft.block.Block; +import net.minecraft.entity.player.EntityPlayer; +import net.minecraft.entity.player.EntityPlayerMP; +import net.minecraft.item.Item; +import net.minecraft.item.ItemStack; +import net.minecraft.nbt.NBTTagCompound; +import net.minecraft.util.EnumChatFormatting; +import net.minecraft.util.IChatComponent; +import net.minecraft.util.StatCollector; +import net.minecraft.world.World; +import net.minecraftforge.common.util.ForgeDirection; + +import com.google.common.collect.ImmutableList; +import com.gtnewhorizon.structurelib.StructureLibAPI; +import com.gtnewhorizon.structurelib.alignment.constructable.ChannelDataAccessor; +import com.gtnewhorizon.structurelib.alignment.constructable.ISurvivalConstructable; +import com.gtnewhorizon.structurelib.structure.IItemSource; +import com.gtnewhorizon.structurelib.structure.IStructureDefinition; +import com.gtnewhorizon.structurelib.structure.IStructureElement; +import com.gtnewhorizon.structurelib.structure.StructureUtility; +import com.gtnewhorizon.structurelib.util.ItemStackPredicate.NBTMode; +import com.gtnewhorizons.modularui.api.drawable.IDrawable; +import com.gtnewhorizons.modularui.api.drawable.UITexture; +import com.gtnewhorizons.modularui.api.screen.ModularWindow; +import com.gtnewhorizons.modularui.api.screen.UIBuildContext; +import com.gtnewhorizons.modularui.common.widget.ButtonWidget; +import com.gtnewhorizons.modularui.common.widget.FakeSyncWidget; + +import bartworks.API.BorosilicateGlass; +import gregtech.api.enums.Dyes; +import gregtech.api.enums.GTValues; +import gregtech.api.enums.Textures.BlockIcons; +import gregtech.api.gui.modularui.GTUITextures; +import gregtech.api.interfaces.IHatchElement; +import gregtech.api.interfaces.ITexture; +import gregtech.api.interfaces.metatileentity.IMetaTileEntity; +import gregtech.api.interfaces.tileentity.IGregTechTileEntity; +import gregtech.api.metatileentity.implementations.MTEEnhancedMultiBlockBase; +import gregtech.api.metatileentity.implementations.MTEHatch; +import gregtech.api.metatileentity.implementations.MTEHatchDynamo; +import gregtech.api.metatileentity.implementations.MTEHatchEnergy; +import gregtech.api.metatileentity.implementations.MTEHatchMaintenance; +import gregtech.api.render.TextureFactory; +import gregtech.api.util.GTUtility; +import gregtech.api.util.IGTHatchAdder; +import gregtech.api.util.MultiblockTooltipBuilder; +import gregtech.common.misc.WirelessNetworkManager; +import kekztech.client.gui.KTUITextures; +import kekztech.common.Blocks; +import kekztech.common.itemBlocks.ItemBlockLapotronicEnergyUnit; +import tectech.thing.metaTileEntity.hatch.MTEHatchDynamoMulti; +import tectech.thing.metaTileEntity.hatch.MTEHatchDynamoTunnel; +import tectech.thing.metaTileEntity.hatch.MTEHatchEnergyMulti; +import tectech.thing.metaTileEntity.hatch.MTEHatchEnergyTunnel; + +public class MTELapotronicSuperCapacitor extends MTEEnhancedMultiBlockBase + implements ISurvivalConstructable { + + private enum TopState { + MayBeTop, + Top, + NotTop + } + + private boolean canUseWireless = false; + private boolean wireless_mode = false; + private boolean not_processed_lsc = true; + private int counter = 1; + private boolean balanced = false; + + private final Queue energyInputValues = new LinkedList<>(); + private final Queue energyOutputValues = new LinkedList<>(); + + private final long max_passive_drain_eu_per_tick_per_uhv_cap = 1_000_000; + private final long max_passive_drain_eu_per_tick_per_uev_cap = 100_000_000; + private final long max_passive_drain_eu_per_tick_per_uiv_cap = (long) Math.pow(10, 10); + private final long max_passive_drain_eu_per_tick_per_umv_cap = (long) Math.pow(10, 12); + + private enum Capacitor { + + IV(2, BigInteger.valueOf(ItemBlockLapotronicEnergyUnit.IV_cap_storage)), + LuV(3, BigInteger.valueOf(ItemBlockLapotronicEnergyUnit.LuV_cap_storage)), + ZPM(4, BigInteger.valueOf(ItemBlockLapotronicEnergyUnit.ZPM_cap_storage)), + UV(5, BigInteger.valueOf(ItemBlockLapotronicEnergyUnit.UV_cap_storage)), + UHV(6, MAX_LONG), + None(0, BigInteger.ZERO), + EV(1, BigInteger.valueOf(ItemBlockLapotronicEnergyUnit.EV_cap_storage)), + UEV(7, MAX_LONG), + UIV(8, BigInteger.valueOf(ItemBlockLapotronicEnergyUnit.UIV_cap_storage)), + UMV(9, ItemBlockLapotronicEnergyUnit.UMV_cap_storage); + + private final int minimalGlassTier; + private final BigInteger providedCapacity; + static final Capacitor[] VALUES = values(); + static final Capacitor[] VALUES_BY_TIER = Arrays.stream(values()) + .sorted(Comparator.comparingInt(Capacitor::getMinimalGlassTier)) + .toArray(Capacitor[]::new); + + Capacitor(int minimalGlassTier, BigInteger providedCapacity) { + this.minimalGlassTier = minimalGlassTier; + this.providedCapacity = providedCapacity; + } + + public int getMinimalGlassTier() { + return minimalGlassTier; + } + + public BigInteger getProvidedCapacity() { + return providedCapacity; + } + + public static int getIndexFromGlassTier(int glassTier) { + for (int index = 0; index < values().length; index++) { + if (values()[index].getMinimalGlassTier() == glassTier) { + return index; + } + } + return -1; + } + } + + private static final String STRUCTURE_PIECE_BASE = "base"; + private static final String STRUCTURE_PIECE_LAYER = "slice"; + private static final String STRUCTURE_PIECE_TOP = "top"; + private static final String STRUCTURE_PIECE_MID = "mid"; + private static final int GLASS_TIER_UNSET = -2; + + private static final Block LSC_PART = Blocks.lscLapotronicEnergyUnit; + private static final Item LSC_PART_ITEM = Item.getItemFromBlock(LSC_PART); + private static final int CASING_META = 0; + private static final int CASING_TEXTURE_ID = (42 << 7) | 127; + + private static final int DURATION_AVERAGE_TICKS = 100; + + // height channel for height. + // glass channel for glass + // capacitor channel for capacitor, but it really just pick whatever capacitor it can find in survival + private static final IStructureDefinition STRUCTURE_DEFINITION = IStructureDefinition + .builder() + .addShape( + STRUCTURE_PIECE_BASE, + transpose( + new String[][] { { "bbbbb", "bbbbb", "bbbbb", "bbbbb", "bbbbb", }, + { "bb~bb", "bbbbb", "bbbbb", "bbbbb", "bbbbb", }, })) + .addShape( + STRUCTURE_PIECE_LAYER, + transpose(new String[][] { { "ggggg", "gcccg", "gcccg", "gcccg", "ggggg", }, })) + .addShape(STRUCTURE_PIECE_TOP, transpose(new String[][] { { "ggggg", "ggggg", "ggggg", "ggggg", "ggggg", }, })) + .addShape(STRUCTURE_PIECE_MID, transpose(new String[][] { { "ggggg", "gCCCg", "gCCCg", "gCCCg", "ggggg", }, })) + .addElement( + 'b', + buildHatchAdder( + MTELapotronicSuperCapacitor.class).atLeast(LSCHatchElement.Energy, LSCHatchElement.Dynamo, Maintenance) + .hatchItemFilterAnd( + (t, h) -> ChannelDataAccessor.getChannelData(h, "glass") < 6 + ? filterByMTEClass(ImmutableList.of(MTEHatchEnergyTunnel.class, MTEHatchDynamoTunnel.class)) + .negate() + : s -> true) + .casingIndex(CASING_TEXTURE_ID) + .dot(1) + .buildAndChain(onElementPass(te -> te.casingAmount++, ofBlock(LSC_PART, CASING_META)))) + .addElement( + 'g', + withChannel( + "glass", + BorosilicateGlass + .ofBoroGlass((byte) GLASS_TIER_UNSET, (te, t) -> te.glassTier = t, te -> te.glassTier))) + .addElement( + 'c', + ofChain( + onlyIf( + te -> te.topState != TopState.NotTop, + onElementPass( + te -> te.topState = TopState.Top, + withChannel( + "glass", + BorosilicateGlass.ofBoroGlass( + (byte) GLASS_TIER_UNSET, + (te, t) -> te.glassTier = t, + te -> te.glassTier)))), + onlyIf( + te -> te.topState != TopState.Top, + onElementPass( + te -> te.topState = TopState.NotTop, + new IStructureElement() { + + @Override + public boolean check(MTELapotronicSuperCapacitor t, World world, int x, int y, int z) { + Block worldBlock = world.getBlock(x, y, z); + int meta = worldBlock.getDamageValue(world, x, y, z); + if (LSC_PART != worldBlock || meta == 0) return false; + t.capacitors[meta - 1]++; + return true; + } + + private int getHint(ItemStack stack) { + return Capacitor.VALUES_BY_TIER[Math.min( + Capacitor.VALUES_BY_TIER.length, + ChannelDataAccessor.getChannelData(stack, "capacitor")) - 1].getMinimalGlassTier() + + 1; + } + + @Override + public boolean spawnHint(MTELapotronicSuperCapacitor t, World world, int x, int y, int z, + ItemStack trigger) { + StructureLibAPI.hintParticle(world, x, y, z, LSC_PART, getHint(trigger)); + return true; + } + + @Override + public boolean placeBlock(MTELapotronicSuperCapacitor t, World world, int x, int y, int z, + ItemStack trigger) { + world.setBlock(x, y, z, LSC_PART, getHint(trigger), 3); + return true; + } + + @Override + public PlaceResult survivalPlaceBlock(MTELapotronicSuperCapacitor t, World world, int x, + int y, int z, ItemStack trigger, IItemSource source, EntityPlayerMP actor, + Consumer chatter) { + if (check(t, world, x, y, z)) return PlaceResult.SKIP; + int glassTier = ChannelDataAccessor.getChannelData(trigger, "glass") + 2; + ItemStack targetStack = source + .takeOne( + s -> s != null && s.stackSize >= 0 + && s.getItem() == LSC_PART_ITEM + && Capacitor.VALUES[Math.min(s.getItemDamage(), Capacitor.VALUES.length) + - 1].getMinimalGlassTier() > glassTier, + true); + if (targetStack == null) return PlaceResult.REJECT; + return StructureUtility.survivalPlaceBlock( + targetStack, + NBTMode.EXACT, + targetStack.stackTagCompound, + true, + world, + x, + y, + z, + source, + actor, + chatter); + } + })))) + .addElement('C', ofBlock(LSC_PART, 1)) + .build(); + + private static final BigInteger MAX_LONG = BigInteger.valueOf(Long.MAX_VALUE); + + private final Set mEnergyHatchesTT = new HashSet<>(); + private final Set mDynamoHatchesTT = new HashSet<>(); + private final Set mEnergyTunnelsTT = new HashSet<>(); + private final Set mDynamoTunnelsTT = new HashSet<>(); + /** + * Count the amount of capacitors of each tier in each slot. Index = meta - 1 + */ + private final int[] capacitors = new int[10]; + + private BigInteger capacity = BigInteger.ZERO; + private BigInteger stored = BigInteger.ZERO; + private long passiveDischargeAmount = 0; + private long inputLastTick = 0; + private long outputLastTick = 0; + private int repairStatusCache = 0; + + private byte glassTier = -1; + private int casingAmount = 0; + private TopState topState = TopState.MayBeTop; + + private long mMaxEUIn = 0; + private long mMaxEUOut = 0; + + public MTELapotronicSuperCapacitor(int aID, String aName, String aNameRegional) { + super(aID, aName, aNameRegional); + } + + public MTELapotronicSuperCapacitor(String aName) { + super(aName); + } + + @Override + public IMetaTileEntity newMetaEntity(IGregTechTileEntity var1) { + return new MTELapotronicSuperCapacitor(super.mName); + } + + @Override + public IStructureDefinition getStructureDefinition() { + return STRUCTURE_DEFINITION; + } + + private void processInputHatch(MTEHatch aHatch, int aBaseCasingIndex) { + mMaxEUIn += aHatch.maxEUInput() * aHatch.maxAmperesIn(); + aHatch.updateTexture(aBaseCasingIndex); + } + + private void processOutputHatch(MTEHatch aHatch, int aBaseCasingIndex) { + mMaxEUOut += aHatch.maxEUOutput() * aHatch.maxAmperesOut(); + aHatch.updateTexture(aBaseCasingIndex); + } + + private boolean addBottomHatches(IGregTechTileEntity aTileEntity, int aBaseCasingIndex) { + if (aTileEntity == null || aTileEntity.isDead()) return false; + IMetaTileEntity aMetaTileEntity = aTileEntity.getMetaTileEntity(); + if (!(aMetaTileEntity instanceof MTEHatch)) return false; + if (aMetaTileEntity instanceof MTEHatchMaintenance) { + ((MTEHatch) aMetaTileEntity).updateTexture(aBaseCasingIndex); + return MTELapotronicSuperCapacitor.this.mMaintenanceHatches.add((MTEHatchMaintenance) aMetaTileEntity); + } else if (aMetaTileEntity instanceof MTEHatchEnergy) { + // Add GT hatches + final MTEHatchEnergy tHatch = ((MTEHatchEnergy) aMetaTileEntity); + processInputHatch(tHatch, aBaseCasingIndex); + return mEnergyHatches.add(tHatch); + } else if (aMetaTileEntity instanceof MTEHatchEnergyTunnel) { + // Add TT Laser hatches + final MTEHatchEnergyTunnel tHatch = ((MTEHatchEnergyTunnel) aMetaTileEntity); + processInputHatch(tHatch, aBaseCasingIndex); + return mEnergyTunnelsTT.add(tHatch); + } else if (aMetaTileEntity instanceof MTEHatchEnergyMulti) { + // Add TT hatches + final MTEHatchEnergyMulti tHatch = (MTEHatchEnergyMulti) aMetaTileEntity; + processInputHatch(tHatch, aBaseCasingIndex); + return mEnergyHatchesTT.add(tHatch); + } else if (aMetaTileEntity instanceof MTEHatchDynamo) { + // Add GT hatches + final MTEHatchDynamo tDynamo = (MTEHatchDynamo) aMetaTileEntity; + processOutputHatch(tDynamo, aBaseCasingIndex); + return mDynamoHatches.add(tDynamo); + } else if (aMetaTileEntity instanceof MTEHatchDynamoTunnel) { + // Add TT Laser hatches + final MTEHatchDynamoTunnel tDynamo = (MTEHatchDynamoTunnel) aMetaTileEntity; + processOutputHatch(tDynamo, aBaseCasingIndex); + return mDynamoTunnelsTT.add(tDynamo); + } else if (aMetaTileEntity instanceof MTEHatchDynamoMulti) { + // Add TT hatches + final MTEHatchDynamoMulti tDynamo = (MTEHatchDynamoMulti) aMetaTileEntity; + processOutputHatch(tDynamo, aBaseCasingIndex); + return mDynamoHatchesTT.add(tDynamo); + } + return false; + } + + private int getUHVCapacitorCount() { + return capacitors[4]; + } + + private int getUEVCapacitorCount() { + return capacitors[7]; + } + + private int getUIVCapacitorCount() { + return capacitors[8]; + } + + private int getUMVCapacitorCount() { + return capacitors[9]; + } + + private int wirelessCapableCapacitors() { + return capacitors[4] + capacitors[7] + capacitors[8] + capacitors[9]; + } + + @Override + protected MultiblockTooltipBuilder createTooltip() { + final MultiblockTooltipBuilder tt = new MultiblockTooltipBuilder(); + tt.addMachineType("Energy Storage") + .addInfo("Loses energy equal to 1% of the total capacity every 24 hours.") + .addInfo( + "Capped at " + EnumChatFormatting.RED + + GTUtility.formatNumbers(max_passive_drain_eu_per_tick_per_uhv_cap) + + EnumChatFormatting.GRAY + + " EU/t passive loss per " + + GTValues.TIER_COLORS[9] + + GTValues.VN[9] + + EnumChatFormatting.GRAY + + " capacitor.") + .addInfo( + "The passive loss increases " + EnumChatFormatting.DARK_RED + + "100" + + EnumChatFormatting.GRAY + + "-fold" + + " for every capacitor tier above.") + .addInfo("Passive loss is multiplied by the number of maintenance issues present.") + .addSeparator() + .addInfo("Glass shell has to be Tier - 3 of the highest capacitor tier.") + .addInfo( + GTValues.TIER_COLORS[8] + GTValues.VN[8] + + EnumChatFormatting.GRAY + + "-tier glass required for " + + EnumChatFormatting.BLUE + + "Tec" + + EnumChatFormatting.DARK_BLUE + + "Tech" + + EnumChatFormatting.GRAY + + " Laser Hatches.") + .addInfo("Add more or better capacitors to increase capacity.") + .addSeparator() + .addInfo("Wireless mode can be enabled by right clicking with a screwdriver.") + .addInfo( + "This mode can only be enabled if you have a " + GTValues.TIER_COLORS[9] + + GTValues.VN[9] + + EnumChatFormatting.GRAY + + "+ capacitor in the multiblock.") + .addInfo( + "When enabled every " + EnumChatFormatting.BLUE + + GTUtility + .formatNumbers(ItemBlockLapotronicEnergyUnit.LSC_time_between_wireless_rebalance_in_ticks) + + EnumChatFormatting.GRAY + + " ticks the LSC will attempt to re-balance against your") + .addInfo("wireless EU network.") + .addInfo( + "If there is less than " + EnumChatFormatting.RED + + GTUtility.formatNumbers(ItemBlockLapotronicEnergyUnit.LSC_wireless_eu_cap) + + EnumChatFormatting.GRAY + + "(" + + GTValues.TIER_COLORS[9] + + GTValues.VN[9] + + EnumChatFormatting.GRAY + + ") EU in the LSC") + .addInfo("it will withdraw from the network and add to the LSC.") + .addInfo("If there is more it will add the EU to the network and remove it from the LSC.") + .addInfo( + "The threshold increases " + EnumChatFormatting.DARK_RED + + "100" + + EnumChatFormatting.GRAY + + "-fold" + + " for every capacitor tier above.") + .addSeparator() + .beginVariableStructureBlock(5, 5, 4, 50, 5, 5, false) + .addStructureInfo("Modular height of 4-50 blocks.") + .addController("Front center bottom") + .addOtherStructurePart("Lapotronic Super Capacitor Casing", "5x2x5 base (at least 17x)") + .addOtherStructurePart( + "Lapotronic Capacitor (" + GTValues.TIER_COLORS[4] + + GTValues.VN[4] + + EnumChatFormatting.GRAY + + "-" + + GTValues.TIER_COLORS[8] + + GTValues.VN[8] + + EnumChatFormatting.GRAY + + "), Ultimate Capacitor (" + + GTValues.TIER_COLORS[9] + + GTValues.VN[9] + + EnumChatFormatting.GRAY + + "-" + + GTValues.TIER_COLORS[12] + + GTValues.VN[12] + + EnumChatFormatting.GRAY + + ")", + "Center 3x(1-47)x3 above base (9-423 blocks)") + .addStructureInfo( + "You can also use the Empty Capacitor to save materials if you use it for less than half the blocks") + .addOtherStructurePart("Borosilicate Glass (any)", "41-777x, Encase capacitor pillar") + .addEnergyHatch("Any casing") + .addDynamoHatch("Any casing") + .addOtherStructurePart( + "Laser Target/Source Hatches", + "Any casing, must be using " + GTValues.TIER_COLORS[8] + + GTValues.VN[8] + + EnumChatFormatting.GRAY + + "-tier glass") + .addStructureInfo("You can have several I/O Hatches") + .addSubChannelUsage("glass", "Borosilicate Glass Tier") + .addSubChannelUsage("capacitor", "Maximum Capacitor Tier") + .addSubChannelUsage("height", "Height of structure") + .addMaintenanceHatch("Any casing") + .toolTipFinisher("KekzTech"); + return tt; + } + + @Override + public ITexture[] getTexture(IGregTechTileEntity aBaseMetaTileEntity, ForgeDirection side, + ForgeDirection forgeDirectionacing, int colorIndex, boolean aActive, boolean aRedstone) { + ITexture[] sTexture = new ITexture[] { + TextureFactory.of(BlockIcons.MACHINE_CASING_FUSION_GLASS, Dyes.getModulation(-1, Dyes._NULL.mRGBa)) }; + if (side == forgeDirectionacing && aActive) { + sTexture = new ITexture[] { TextureFactory + .of(BlockIcons.MACHINE_CASING_FUSION_GLASS_YELLOW, Dyes.getModulation(-1, Dyes._NULL.mRGBa)) }; + } + return sTexture; + } + + private UUID global_energy_user_uuid; + + @Override + public void onPreTick(IGregTechTileEntity tileEntity, long aTick) { + super.onPreTick(tileEntity, aTick); + + // On first tick (aTick restarts from 0 upon world reload). + if (not_processed_lsc && tileEntity.isServerSide()) { + // Add user to wireless network. + WirelessNetworkManager.strongCheckOrAddUser(tileEntity.getOwnerUuid()); + + // Get team UUID. + global_energy_user_uuid = tileEntity.getOwnerUuid(); + + not_processed_lsc = false; + } + } + + @Override + public boolean isCorrectMachinePart(ItemStack stack) { + return true; + } + + @Override + public boolean checkRecipe(ItemStack stack) { + this.mProgresstime = 1; + this.mMaxProgresstime = 1; + this.mEUt = 0; + this.mEfficiencyIncrease = 10000; + return true; + } + + @Override + public boolean checkMachine(IGregTechTileEntity thisController, ItemStack guiSlotItem) { + WirelessNetworkManager.strongCheckOrAddUser(thisController.getOwnerUuid()); + + // Reset capacitor counts + Arrays.fill(capacitors, 0); + // Clear TT hatches + mEnergyHatchesTT.clear(); + mDynamoHatchesTT.clear(); + mEnergyTunnelsTT.clear(); + mDynamoTunnelsTT.clear(); + + mMaxEUIn = 0; + mMaxEUOut = 0; + + glassTier = GLASS_TIER_UNSET; + casingAmount = 0; + + if (!checkPiece(STRUCTURE_PIECE_BASE, 2, 1, 0)) return false; + + if (casingAmount < 17) return false; + + topState = TopState.NotTop; // need at least one layer of capacitor to form, obviously + int layer = 2; + while (true) { + if (!checkPiece(STRUCTURE_PIECE_LAYER, 2, layer, 0)) return false; + layer++; + if (topState == TopState.Top) break; // top found, break out + topState = TopState.MayBeTop; + if (layer > 50) return false; // too many layers + } + + // Make sure glass tier is T-2 of the highest tier capacitor in the structure + // Count down from the highest tier until an entry is found + // Borosilicate glass after 5 are just recolours of 0 + for (int highestGlassTier = capacitors.length - 1; highestGlassTier >= 0; highestGlassTier--) { + int highestCapacitor = Capacitor.getIndexFromGlassTier(highestGlassTier); + if (capacitors[highestCapacitor] > 0) { + if (Capacitor.VALUES[highestCapacitor].getMinimalGlassTier() > glassTier) return false; + break; + } + } + + // Glass has to be at least UV-tier to allow TT Laser hatches + if (glassTier < 8) { + if (mEnergyTunnelsTT.size() > 0 || mDynamoTunnelsTT.size() > 0) return false; + } + + // Check if enough (more than 50%) non-empty caps + if (capacitors[5] > capacitors[0] + capacitors[1] + + capacitors[2] + + capacitors[3] + + getUHVCapacitorCount() + + capacitors[6] + + getUEVCapacitorCount() + + getUIVCapacitorCount() + + getUMVCapacitorCount()) return false; + + // Calculate total capacity + capacity = BigInteger.ZERO; + for (int i = 0; i < capacitors.length; i++) { + int count = capacitors[i]; + capacity = capacity.add( + Capacitor.VALUES[i].getProvidedCapacity() + .multiply(BigInteger.valueOf(count))); + } + // Calculate how much energy to void each tick + passiveDischargeAmount = recalculateLossWithMaintenance(getRepairStatus()); + return mMaintenanceHatches.size() == 1; + } + + @Override + public void construct(ItemStack stackSize, boolean hintsOnly) { + int layer = min(stackSize.stackSize + 3, 50); + buildPiece(STRUCTURE_PIECE_BASE, stackSize, hintsOnly, 2, 1, 0); + for (int i = 2; i < layer - 1; i++) buildPiece(STRUCTURE_PIECE_MID, stackSize, hintsOnly, 2, i, 0); + buildPiece(STRUCTURE_PIECE_TOP, stackSize, hintsOnly, 2, layer - 1, 0); + } + + @Override + public int survivalConstruct(ItemStack stackSize, int elementBudget, IItemSource source, EntityPlayerMP actor) { + if (mMachine) return -1; + int layer = Math.min(ChannelDataAccessor.getChannelData(stackSize, "height") + 3, 50); + int built; + built = survivialBuildPiece( + STRUCTURE_PIECE_BASE, + stackSize, + 2, + 1, + 0, + elementBudget, + source, + actor, + false, + true); + if (built >= 0) return built; + for (int i = 2; i < layer - 1; i++) built = survivialBuildPiece( + STRUCTURE_PIECE_MID, + stackSize, + 2, + i, + 0, + elementBudget, + source, + actor, + false, + true); + if (built >= 0) return built; + return survivialBuildPiece( + STRUCTURE_PIECE_TOP, + stackSize, + 2, + layer - 1, + 0, + elementBudget, + source, + actor, + false, + true); + } + + @Override + public boolean onRunningTick(ItemStack stack) { + // Reset I/O cache + inputLastTick = 0; + outputLastTick = 0; + + long temp_stored = 0L; + + // Draw energy from GT hatches + for (MTEHatchEnergy eHatch : super.mEnergyHatches) { + if (eHatch == null || !eHatch.isValid()) { + continue; + } + final long power = getPowerToDraw(eHatch.maxEUInput() * eHatch.maxAmperesIn()); + if (eHatch.getEUVar() >= power) { + eHatch.setEUVar(eHatch.getEUVar() - power); + temp_stored += power; + inputLastTick += power; + } + } + + // Output energy to GT hatches + for (MTEHatchDynamo eDynamo : super.mDynamoHatches) { + if (eDynamo == null || !eDynamo.isValid()) { + continue; + } + final long power = getPowerToPush(eDynamo.maxEUOutput() * eDynamo.maxAmperesOut()); + if (power <= eDynamo.maxEUStore() - eDynamo.getEUVar()) { + eDynamo.setEUVar(eDynamo.getEUVar() + power); + temp_stored -= power; + outputLastTick += power; + } + } + + // Draw energy from TT hatches + for (MTEHatchEnergyMulti eHatch : mEnergyHatchesTT) { + if (eHatch == null || !eHatch.isValid()) { + continue; + } + final long power = getPowerToDraw(eHatch.maxEUInput() * eHatch.maxAmperesIn()); + if (eHatch.getEUVar() >= power) { + eHatch.setEUVar(eHatch.getEUVar() - power); + temp_stored += power; + inputLastTick += power; + } + } + + // Output energy to TT hatches + for (MTEHatchDynamoMulti eDynamo : mDynamoHatchesTT) { + if (eDynamo == null || !eDynamo.isValid()) { + continue; + } + final long power = getPowerToPush(eDynamo.maxEUOutput() * eDynamo.maxAmperesOut()); + if (power <= eDynamo.maxEUStore() - eDynamo.getEUVar()) { + eDynamo.setEUVar(eDynamo.getEUVar() + power); + temp_stored -= power; + outputLastTick += power; + } + } + + // Draw energy from TT Laser hatches + for (MTEHatchEnergyTunnel eHatch : mEnergyTunnelsTT) { + if (eHatch == null || !eHatch.isValid()) { + continue; + } + final long ttLaserWattage = eHatch.maxEUInput() * eHatch.Amperes - (eHatch.Amperes / 20); + final long power = getPowerToDraw(ttLaserWattage); + if (eHatch.getEUVar() >= power) { + eHatch.setEUVar(eHatch.getEUVar() - power); + temp_stored += power; + inputLastTick += power; + } + } + + // Output energy to TT Laser hatches + for (MTEHatchDynamoTunnel eDynamo : mDynamoTunnelsTT) { + if (eDynamo == null || !eDynamo.isValid()) { + continue; + } + final long ttLaserWattage = eDynamo.maxEUOutput() * eDynamo.Amperes - (eDynamo.Amperes / 20); + final long power = getPowerToPush(ttLaserWattage); + if (power <= eDynamo.maxEUStore() - eDynamo.getEUVar()) { + eDynamo.setEUVar(eDynamo.getEUVar() + power); + temp_stored -= power; + outputLastTick += power; + } + } + + if (wirelessCapableCapacitors() <= 0) { + wireless_mode = false; + } + + // Every LSC_time_between_wireless_rebalance_in_ticks check against wireless network for re-balancing. + counter++; + if (wireless_mode && (counter >= ItemBlockLapotronicEnergyUnit.LSC_time_between_wireless_rebalance_in_ticks)) { + + // Reset tick counter. + counter = rebalance(); + } + + // Lose some energy. + // Re-calculate if the repair status changed. + if (super.getRepairStatus() != repairStatusCache) { + passiveDischargeAmount = recalculateLossWithMaintenance(super.getRepairStatus()); + } + + // This will break if you transfer more than 2^63 EU/t, so don't do that. Thanks <3 + temp_stored -= passiveDischargeAmount; + stored = stored.add(BigInteger.valueOf(temp_stored)); + + // Check that the machine has positive EU stored. + stored = (stored.compareTo(BigInteger.ZERO) <= 0) ? BigInteger.ZERO : stored; + + IGregTechTileEntity tBMTE = this.getBaseMetaTileEntity(); + + tBMTE.injectEnergyUnits(ForgeDirection.UNKNOWN, inputLastTick, 1L); + tBMTE.drainEnergyUnits(ForgeDirection.UNKNOWN, outputLastTick, 1L); + + // Add I/O values to Queues + if (energyInputValues.size() > DURATION_AVERAGE_TICKS) { + energyInputValues.remove(); + } + energyInputValues.offer(inputLastTick); + + if (energyOutputValues.size() > DURATION_AVERAGE_TICKS) { + energyOutputValues.remove(); + } + + energyOutputValues.offer(outputLastTick); + + return true; + } + + private int rebalance() { + + balanced = true; + + // Find difference. + BigInteger transferred_eu = stored.subtract( + (ItemBlockLapotronicEnergyUnit.LSC_wireless_eu_cap.multiply(BigInteger.valueOf(getUHVCapacitorCount()))) + .add( + ItemBlockLapotronicEnergyUnit.UEV_wireless_eu_cap + .multiply(BigInteger.valueOf(getUEVCapacitorCount()))) + .add( + ItemBlockLapotronicEnergyUnit.UIV_wireless_eu_cap + .multiply(BigInteger.valueOf(getUIVCapacitorCount()))) + .add( + ItemBlockLapotronicEnergyUnit.UMV_wireless_eu_cap + .multiply(BigInteger.valueOf(getUMVCapacitorCount())))); + + if (transferred_eu.signum() == 1) { + inputLastTick += transferred_eu.longValue(); + } else { + outputLastTick += transferred_eu.longValue(); + } + + // If that difference can be added then do so. + if (WirelessNetworkManager.addEUToGlobalEnergyMap(global_energy_user_uuid, transferred_eu)) { + // If it succeeds there was sufficient energy so set the internal capacity as such. + stored = ItemBlockLapotronicEnergyUnit.LSC_wireless_eu_cap + .multiply(BigInteger.valueOf(getUHVCapacitorCount())) + .add( + ItemBlockLapotronicEnergyUnit.UEV_wireless_eu_cap + .multiply(BigInteger.valueOf(getUEVCapacitorCount())) + .add( + ItemBlockLapotronicEnergyUnit.UIV_wireless_eu_cap + .multiply(BigInteger.valueOf(getUIVCapacitorCount()))) + .add( + ItemBlockLapotronicEnergyUnit.UMV_wireless_eu_cap + .multiply(BigInteger.valueOf(getUMVCapacitorCount())))); + } + + return 1; + } + + /** + * To be called whenever the maintenance status changes or the capacity was recalculated + * + * @param repairStatus This machine's repair status + * @return new BigInteger instance for passiveDischargeAmount + */ + private long recalculateLossWithMaintenance(int repairStatus) { + repairStatusCache = repairStatus; + + long temp_capacity_divided = 0; + + if (wirelessCapableCapacitors() == 0) { + temp_capacity_divided = capacity.divide(BigInteger.valueOf(100L * 86400L * 20L)) + .longValue(); + } + + // Passive loss is multiplied by number of UHV+ caps. Minimum of 1 otherwise loss is 0 for non-UHV+ caps + // calculations. + if (wirelessCapableCapacitors() != 0) { + temp_capacity_divided = getUHVCapacitorCount() * max_passive_drain_eu_per_tick_per_uhv_cap + + getUEVCapacitorCount() * max_passive_drain_eu_per_tick_per_uev_cap + + getUIVCapacitorCount() * max_passive_drain_eu_per_tick_per_uiv_cap + + getUMVCapacitorCount() * max_passive_drain_eu_per_tick_per_umv_cap; + } + + // Passive loss is multiplied by number of maintenance issues. + long total_passive_loss = temp_capacity_divided * (getIdealStatus() - repairStatus + 1); + + // Maximum of 100,000 EU/t drained per UHV cell. The logic is 1% of EU capacity should be drained every 86400 + // seconds (1 day). + return total_passive_loss; + } + + /** + * Calculate how much EU to draw from an Energy Hatch + * + * @param hatchWatts Hatch amperage * voltage + * @return EU amount + */ + private long getPowerToDraw(long hatchWatts) { + final BigInteger remcapActual = capacity.subtract(stored); + final BigInteger recampLimited = (MAX_LONG.compareTo(remcapActual) > 0) ? remcapActual : MAX_LONG; + return min(hatchWatts, recampLimited.longValue()); + } + + /** + * Calculate how much EU to push into a Dynamo Hatch + * + * @param hatchWatts Hatch amperage * voltage + * @return EU amount + */ + private long getPowerToPush(long hatchWatts) { + final BigInteger remStoredLimited = (MAX_LONG.compareTo(stored) > 0) ? stored : MAX_LONG; + return min(hatchWatts, remStoredLimited.longValue()); + } + + private long getAvgIn() { + long sum = 0L; + for (long l : energyInputValues) { + sum += l; + } + return sum / Math.max(energyInputValues.size(), 1); + } + + private long getAvgOut() { + long sum = 0L; + for (long l : energyOutputValues) { + sum += l; + } + return sum / Math.max(energyOutputValues.size(), 1); + } + + @Override + public String[] getInfoData() { + NumberFormat nf = NumberFormat.getNumberInstance(); + int secInterval = DURATION_AVERAGE_TICKS / 20; + + // Caching avgin and avgout + double avgIn = getAvgIn(); + double avgOut = getAvgOut(); + + final ArrayList ll = new ArrayList<>(); + ll.add(EnumChatFormatting.YELLOW + "Operational Data:" + EnumChatFormatting.RESET); + ll.add("EU Stored: " + nf.format(stored) + " EU"); + ll.add("EU Stored: " + toStandardForm(stored) + " EU"); + ll.add("Used Capacity: " + toPercentageFrom(stored, capacity)); + ll.add("Total Capacity: " + nf.format(capacity) + " EU"); + ll.add("Total Capacity: " + toStandardForm(capacity) + " EU"); + ll.add("Passive Loss: " + nf.format(passiveDischargeAmount) + " EU/t"); + ll.add("EU IN: " + GTUtility.formatNumbers(inputLastTick) + " EU/t"); + ll.add("EU OUT: " + GTUtility.formatNumbers(outputLastTick) + " EU/t"); + ll.add("Avg EU IN: " + nf.format(avgIn) + " (last " + secInterval + " seconds)"); + ll.add("Avg EU OUT: " + nf.format(avgOut) + " (last " + secInterval + " seconds)"); + + // Check if the system is charging or discharging + if (avgIn > avgOut) { + // Calculate time to full if charging + if (avgIn != 0) { + double timeToFull = (capacity.longValue() - stored.longValue()) / avgIn / 20; + String timeToFullString = formatTime(timeToFull); + ll.add("Time to Full: " + timeToFullString); + } + } else { + // Calculate time to empty if discharging + if (avgOut != 0) { + double timeToEmpty = stored.longValue() / avgOut / 20; + String timeToEmptyString = formatTime(timeToEmpty); + ll.add("Time to Empty: " + timeToEmptyString); + } + } + ll.add( + "Maintenance Status: " + ((super.getRepairStatus() == super.getIdealStatus()) + ? EnumChatFormatting.GREEN + "Working perfectly" + EnumChatFormatting.RESET + : EnumChatFormatting.RED + "Has Problems" + EnumChatFormatting.RESET)); + ll.add( + "Wireless mode: " + (wireless_mode ? EnumChatFormatting.GREEN + "enabled" + EnumChatFormatting.RESET + : EnumChatFormatting.RED + "disabled" + EnumChatFormatting.RESET)); + ll.add( + GTValues.TIER_COLORS[9] + GTValues.VN[9] + + EnumChatFormatting.RESET + + " Capacitors detected: " + + getUHVCapacitorCount()); + ll.add( + GTValues.TIER_COLORS[10] + GTValues.VN[10] + + EnumChatFormatting.RESET + + " Capacitors detected: " + + getUEVCapacitorCount()); + ll.add( + GTValues.TIER_COLORS[11] + GTValues.VN[11] + + EnumChatFormatting.RESET + + " Capacitors detected: " + + getUIVCapacitorCount()); + ll.add( + GTValues.TIER_COLORS[12] + GTValues.VN[12] + + EnumChatFormatting.RESET + + " Capacitors detected: " + + getUMVCapacitorCount()); + ll.add( + "Total wireless EU: " + EnumChatFormatting.RED + + nf.format(WirelessNetworkManager.getUserEU(global_energy_user_uuid)) + + " EU"); + ll.add( + "Total wireless EU: " + EnumChatFormatting.RED + + toStandardForm(WirelessNetworkManager.getUserEU(global_energy_user_uuid)) + + " EU"); + + final String[] a = new String[ll.size()]; + return ll.toArray(a); + } + + // Method to format time in seconds, minutes, days, and years + private String formatTime(double time) { + if (time < 1) { + return "Completely " + (time < 0 ? "empty" : "full"); + } else if (time < 60) { + return String.format("%.2f seconds", time); + } else if (time < 3600) { + return String.format("%.2f minutes", time / 60); + } else if (time < 86400) { + return String.format("%.2f hours", time / 3600); + } else if (time < 31536000) { + return String.format("%.2f days", time / 86400); + } else { + return String.format("%.2f years", time / 31536000); + } + } + + @Override + public void saveNBTData(NBTTagCompound nbt) { + nbt = (nbt == null) ? new NBTTagCompound() : nbt; + + nbt.setByteArray("capacity", capacity.toByteArray()); + nbt.setByteArray("stored", stored.toByteArray()); + nbt.setBoolean("wireless_mode", wireless_mode); + nbt.setInteger("wireless_mode_cooldown", counter); + + super.saveNBTData(nbt); + } + + @Override + public void loadNBTData(NBTTagCompound nbt) { + nbt = (nbt == null) ? new NBTTagCompound() : nbt; + + capacity = new BigInteger(nbt.getByteArray("capacity")); + stored = new BigInteger(nbt.getByteArray("stored")); + wireless_mode = nbt.getBoolean("wireless_mode"); + counter = nbt.getInteger("wireless_mode_cooldown"); + + super.loadNBTData(nbt); + } + + @Override + public boolean isGivingInformation() { + return true; + } + + @Override + public int getMaxEfficiency(ItemStack stack) { + return 10000; + } + + @Override + public int getPollutionPerTick(ItemStack stack) { + return 0; + } + + @Override + public int getDamageToComponent(ItemStack stack) { + return 0; + } + + @Override + public boolean explodesOnComponentBreak(ItemStack stack) { + return false; + } + + // called by the getEUCapacity() function in BaseMetaTileEntity + @Override + public long maxEUStore() { + return capacity.longValue(); + } + + // called by the getEUStored() function in BaseMetaTileEntity + @Override + public long getEUVar() { + return stored.longValue(); + } + + /* + * all of these are needed for the injectEnergyUnits() and drainEnergyUnits() in IGregTechTileEntity + */ + @Override + public long maxEUInput() { + if (wireless_mode) { + return Long.MAX_VALUE; + } else { + return mMaxEUIn; + } + } + + @Override + public long maxAmperesIn() { + return 1L; + } + + @Override + public long maxEUOutput() { + if (wireless_mode) { + return Long.MAX_VALUE; + } else { + return mMaxEUOut; + } + } + + @Override + public long maxAmperesOut() { + return 1L; + } + + @Override + public boolean isEnetInput() { + return true; + } + + @Override + public boolean isEnetOutput() { + return true; + } + + protected boolean canUseWireless() { + return wirelessCapableCapacitors() != 0; + } + + @Override + public void onScrewdriverRightClick(ForgeDirection side, EntityPlayer aPlayer, float aX, float aY, float aZ) { + if (canUseWireless()) { + wireless_mode = !wireless_mode; + GTUtility.sendChatToPlayer(aPlayer, "Wireless network mode " + (wireless_mode ? "enabled." : "disabled.")); + } else { + GTUtility.sendChatToPlayer( + aPlayer, + "Wireless mode cannot be enabled without at least 1 " + GTValues.TIER_COLORS[9] + + GTValues.VN[9] + + EnumChatFormatting.RESET + + "+ capacitor."); + wireless_mode = false; + } + } + + @Override + public void addUIWidgets(ModularWindow.Builder builder, UIBuildContext buildContext) { + super.addUIWidgets(builder, buildContext); + builder.widget(new ButtonWidget().setOnClick((clickData, widget) -> { + if (!widget.isClient()) { + canUseWireless = canUseWireless(); + } + if (canUseWireless) { + wireless_mode = !wireless_mode; + } + }) + .setPlayClickSound(true) + .setBackground(() -> { + List ret = new ArrayList<>(); + ret.add(GTUITextures.BUTTON_STANDARD); + if (canUseWireless) { + if (wireless_mode) { + ret.add(KTUITextures.OVERLAY_BUTTON_WIRELESS_ON); + } else { + ret.add(KTUITextures.OVERLAY_BUTTON_WIRELESS_OFF); + } + } else { + ret.add(KTUITextures.OVERLAY_BUTTON_WIRELESS_OFF_DISABLED); + } + return ret.toArray(new IDrawable[0]); + }) + .setPos(80, 91) + .setSize(16, 16) + .addTooltip(StatCollector.translateToLocal("gui.kekztech_lapotronicenergyunit.wireless")) + .setTooltipShowUpDelay(TOOLTIP_DELAY)) + .widget(new FakeSyncWidget.BooleanSyncer(() -> wireless_mode, val -> wireless_mode = val)) + .widget(new FakeSyncWidget.BooleanSyncer(this::canUseWireless, val -> canUseWireless = val)) + .widget(new ButtonWidget().setOnClick((clickData, widget) -> { + if (mMachine && wireless_mode && canUseWireless && !balanced) { + counter = rebalance(); + } + }) + .setPlayClickSound(true) + .setBackground(() -> { + List ret = new ArrayList<>(); + ret.add(GTUITextures.BUTTON_STANDARD); + ret.add(KTUITextures.OVERLAY_BUTTON_WIRELESS_REBALANCE); + return ret.toArray(new IDrawable[0]); + }) + .setPos(98, 91) + .setSize(16, 16) + .setEnabled((widget) -> wireless_mode && canUseWireless && !balanced) + .addTooltip(StatCollector.translateToLocal("gui.kekztech_lapotronicenergyunit.wireless_rebalance")) + .setTooltipShowUpDelay(TOOLTIP_DELAY)); + } + + private enum LSCHatchElement implements IHatchElement { + + Energy(MTEHatchEnergyMulti.class, MTEHatchEnergy.class) { + + @Override + public long count(MTELapotronicSuperCapacitor t) { + return t.mEnergyHatches.size() + t.mEnergyHatchesTT.size() + t.mEnergyTunnelsTT.size(); + } + }, + Dynamo(MTEHatchDynamoMulti.class, MTEHatchDynamo.class) { + + @Override + public long count(MTELapotronicSuperCapacitor t) { + return t.mDynamoHatches.size() + t.mDynamoHatchesTT.size() + t.mDynamoTunnelsTT.size(); + } + },; + + private final List> mteClasses; + + @SafeVarargs + LSCHat