package micdoodle8.mods.galacticraft.core.tile; import micdoodle8.mods.galacticraft.api.item.IItemOxygenSupply; import micdoodle8.mods.galacticraft.api.vector.BlockVec3; import micdoodle8.mods.galacticraft.core.GCItems; import micdoodle8.mods.galacticraft.core.blocks.BlockOxygenSealer; import micdoodle8.mods.galacticraft.core.energy.item.ItemElectricBase; import micdoodle8.mods.galacticraft.core.fluid.OxygenPressureProtocol; import micdoodle8.mods.galacticraft.core.fluid.ThreadFindSeal; import micdoodle8.mods.galacticraft.core.inventory.IInventoryDefaults; import micdoodle8.mods.galacticraft.core.energy.tile.EnergyStorageTile; import micdoodle8.mods.galacticraft.core.util.FluidUtil; import micdoodle8.mods.galacticraft.core.util.GCCoreUtil; import micdoodle8.mods.miccore.Annotations.NetworkedField; import net.minecraft.block.Block; import net.minecraft.entity.player.EntityPlayer; import net.minecraft.inventory.ISidedInventory; import net.minecraft.item.ItemStack; import net.minecraft.nbt.NBTTagCompound; import net.minecraft.nbt.NBTTagList; import net.minecraft.util.BlockPos; import net.minecraft.util.EnumFacing; import net.minecraft.world.World; import net.minecraftforge.fml.relauncher.Side; import java.util.ArrayList; import java.util.EnumSet; import java.util.HashMap; public class TileEntityOxygenSealer extends TileEntityOxygen implements IInventoryDefaults, ISidedInventory { @NetworkedField(targetSide = Side.CLIENT) public boolean sealed; public boolean lastSealed = false; public boolean lastDisabled = false; @NetworkedField(targetSide = Side.CLIENT) public boolean active; private ItemStack[] containingItems = new ItemStack[3]; public ThreadFindSeal threadSeal; @NetworkedField(targetSide = Side.CLIENT) public int stopSealThreadCooldown; @NetworkedField(targetSide = Side.CLIENT) public int threadCooldownTotal; @NetworkedField(targetSide = Side.CLIENT) public boolean calculatingSealed; public static int countEntities = 0; private static int countTemp = 0; private static boolean sealerCheckedThisTick = false; public static ArrayList<TileEntityOxygenSealer> loadedTiles = new ArrayList(); private static final int UNSEALED_OXYGENPERTICK = 12; public TileEntityOxygenSealer() { super(10000, UNSEALED_OXYGENPERTICK); this.noRedstoneControl = true; this.storage.setMaxExtract(5.0F); //Half of a standard machine's power draw this.storage.setMaxReceive(25.0F); this.storage.setCapacity(EnergyStorageTile.STANDARD_CAPACITY * 2); //Large capacity so it can keep working for a while even if chunk unloads affect its power supply } @Override public void onLoad() { if (!this.worldObj.isRemote) { if (!TileEntityOxygenSealer.loadedTiles.contains(this)) { TileEntityOxygenSealer.loadedTiles.add(this); } this.stopSealThreadCooldown = 126 + countEntities; } } @Override public void invalidate() { if (!this.worldObj.isRemote) { TileEntityOxygenSealer.loadedTiles.remove(this); } super.invalidate(); } @Override public void onChunkUnload() { if (!this.worldObj.isRemote) { TileEntityOxygenSealer.loadedTiles.remove(this); } super.onChunkUnload(); } public int getScaledThreadCooldown(int i) { if (this.active) { return Math.min(i, (int) Math.floor(this.stopSealThreadCooldown * i / (double) this.threadCooldownTotal)); } return 0; } public int getFindSealChecks() { if (!this.active || this.getOxygenStored() < this.oxygenPerTick || !this.hasEnoughEnergyToRun) { return 0; } BlockPos posAbove = new BlockPos(this.getPos().getX(), this.getPos().getY() + 1, this.getPos().getZ()); Block blockAbove = this.worldObj.getBlockState(posAbove).getBlock(); if (!(blockAbove.isAir(this.worldObj, posAbove)) && !OxygenPressureProtocol.canBlockPassAir(this.worldObj, blockAbove, this.getPos().up(), EnumFacing.UP)) { // The vent is blocked return 0; } return 1250; } public boolean thermalControlEnabled() { ItemStack oxygenItemStack = this.getStackInSlot(2); return oxygenItemStack != null && oxygenItemStack.getItem() == GCItems.basicItem && oxygenItemStack.getItemDamage() == 20 && this.hasEnoughEnergyToRun && !this.disabled; } @Override public void update() { if (!this.worldObj.isRemote) { ItemStack oxygenItemStack = this.getStackInSlot(1); if (oxygenItemStack != null && oxygenItemStack.getItem() instanceof IItemOxygenSupply) { IItemOxygenSupply oxygenItem = (IItemOxygenSupply) oxygenItemStack.getItem(); int oxygenDraw = (int) Math.floor(Math.min(UNSEALED_OXYGENPERTICK * 2.5F, this.getMaxOxygenStored() - this.getOxygenStored())); this.setOxygenStored(getOxygenStored() + oxygenItem.discharge(oxygenItemStack, oxygenDraw)); if (this.getOxygenStored() > this.getMaxOxygenStored()) { this.setOxygenStored(this.getOxygenStored()); } } if (this.thermalControlEnabled()) { if (this.storage.getMaxExtract() != 20.0F) { this.storage.setMaxExtract(20.0F); } } else if (this.storage.getMaxExtract() != 5.0F) { this.storage.setMaxExtract(5.0F); this.storage.setMaxReceive(25.0F); } } this.oxygenPerTick = this.sealed ? 2 : UNSEALED_OXYGENPERTICK; super.update(); if (!this.worldObj.isRemote) { // Some code to count the number of Oxygen Sealers being updated, // tick by tick - needed for queueing TileEntityOxygenSealer.countTemp++; this.active = this.getOxygenStored() >= 1 && this.hasEnoughEnergyToRun && !this.disabled; if (this.disabled != this.lastDisabled) { this.lastDisabled = this.disabled; if (!this.disabled) this.stopSealThreadCooldown = this.threadCooldownTotal * 3 / 5; } //TODO: if multithreaded, this codeblock should not run if the current threadSeal is flagged looping if (this.stopSealThreadCooldown > 0) { this.stopSealThreadCooldown--; } else if (!TileEntityOxygenSealer.sealerCheckedThisTick) { // This puts any Sealer which is updated to the back of the queue for updates this.threadCooldownTotal = this.stopSealThreadCooldown = 75 + TileEntityOxygenSealer.countEntities; if (this.active || this.sealed) { TileEntityOxygenSealer.sealerCheckedThisTick = true; OxygenPressureProtocol.updateSealerStatus(this); } } //TODO: if multithreaded, this.threadSeal needs to be atomic if (this.threadSeal != null) { if (this.threadSeal.looping.get()) { this.calculatingSealed = this.active; } else { this.calculatingSealed = false; this.sealed = this.threadSeal.sealedFinal.get(); } } else { this.calculatingSealed = true; //Give an initial 'Check pending' in GUI when first placed } this.lastSealed = this.sealed; } } public static void onServerTick() { TileEntityOxygenSealer.countEntities = TileEntityOxygenSealer.countTemp; TileEntityOxygenSealer.countTemp = 0; TileEntityOxygenSealer.sealerCheckedThisTick = false; } @Override public void readFromNBT(NBTTagCompound par1NBTTagCompound) { super.readFromNBT(par1NBTTagCompound); final NBTTagList var2 = par1NBTTagCompound.getTagList("Items", 10); this.containingItems = new ItemStack[this.getSizeInventory()]; for (int var3 = 0; var3 < var2.tagCount(); ++var3) { final NBTTagCompound var4 = var2.getCompoundTagAt(var3); final int var5 = var4.getByte("Slot") & 255; if (var5 < this.containingItems.length) { this.containingItems[var5] = ItemStack.loadItemStackFromNBT(var4); } } } @Override public void writeToNBT(NBTTagCompound par1NBTTagCompound) { super.writeToNBT(par1NBTTagCompound); final NBTTagList list = new NBTTagList(); for (int var3 = 0; var3 < this.containingItems.length; ++var3) { if (this.containingItems[var3] != null) { final NBTTagCompound var4 = new NBTTagCompound(); var4.setByte("Slot", (byte) var3); this.containingItems[var3].writeToNBT(var4); list.appendTag(var4); } } par1NBTTagCompound.setTag("Items", list); } @Override public int getSizeInventory() { return this.containingItems.length; } @Override public ItemStack getStackInSlot(int par1) { return this.containingItems[par1]; } @Override public ItemStack decrStackSize(int par1, int par2) { if (this.containingItems[par1] != null) { ItemStack var3; if (this.containingItems[par1].stackSize <= par2) { var3 = this.containingItems[par1]; this.containingItems[par1] = null; return var3; } else { var3 = this.containingItems[par1].splitStack(par2); if (this.containingItems[par1].stackSize == 0) { this.containingItems[par1] = null; } return var3; } } else { return null; } } @Override public ItemStack removeStackFromSlot(int par1) { if (this.containingItems[par1] != null) { final ItemStack var2 = this.containingItems[par1]; this.containingItems[par1] = null; return var2; } else { return null; } } @Override public void setInventorySlotContents(int par1, ItemStack par2ItemStack) { this.containingItems[par1] = par2ItemStack; if (par2ItemStack != null && par2ItemStack.stackSize > this.getInventoryStackLimit()) { par2ItemStack.stackSize = this.getInventoryStackLimit(); } } @Override public String getName() { return GCCoreUtil.translate("container.oxygensealer.name"); } @Override public int getInventoryStackLimit() { return 64; } @Override public boolean isUseableByPlayer(EntityPlayer par1EntityPlayer) { return this.worldObj.getTileEntity(this.getPos()) == this && par1EntityPlayer.getDistanceSq(this.getPos().getX() + 0.5D, this.getPos().getY() + 0.5D, this.getPos().getZ() + 0.5D) <= 64.0D; } // ISidedInventory Implementation: @Override public int[] getSlotsForFace(EnumFacing side) { return new int[] { 0, 1 }; } @Override public boolean canInsertItem(int slotID, ItemStack itemstack, EnumFacing side) { if (this.isItemValidForSlot(slotID, itemstack)) { switch (slotID) { case 0: return itemstack.getItem() instanceof ItemElectricBase && ((ItemElectricBase) itemstack.getItem()).getElectricityStored(itemstack) > 0; case 1: return itemstack.getItemDamage() < itemstack.getItem().getMaxDamage(); case 2: return itemstack.getItem() == GCItems.basicItem && itemstack.getItemDamage() == 20; default: return false; } } return false; } @Override public boolean canExtractItem(int slotID, ItemStack itemstack, EnumFacing side) { switch (slotID) { case 0: return itemstack.getItem() instanceof ItemElectricBase && ((ItemElectricBase) itemstack.getItem()).getElectricityStored(itemstack) <= 0; case 1: return FluidUtil.isEmptyContainer(itemstack); default: return false; } } @Override public boolean hasCustomName() { return true; } @Override public boolean isItemValidForSlot(int slotID, ItemStack itemstack) { if (itemstack == null) { return false; } if (slotID == 0) { return ItemElectricBase.isElectricItem(itemstack.getItem()); } if (slotID == 1) { return itemstack.getItem() instanceof IItemOxygenSupply; } if (slotID == 2) { return itemstack.getItem() == GCItems.basicItem && itemstack.getItemDamage() == 20; } return false; } @Override public boolean shouldUseEnergy() { return this.getOxygenStored() > this.oxygenPerTick && !this.getDisabled(0); } @Override public EnumFacing getFront() { return this.worldObj.getBlockState(getPos()).getValue(BlockOxygenSealer.FACING); } @Override public EnumFacing getElectricInputDirection() { return getFront().rotateY(); } @Override public ItemStack getBatteryInSlot() { return this.getStackInSlot(0); } @Override public boolean shouldUseOxygen() { return this.hasEnoughEnergyToRun && this.active; } @Override public EnumSet<EnumFacing> getOxygenInputDirections() { return EnumSet.of(this.getElectricInputDirection().getOpposite()); } @Override public EnumSet<EnumFacing> getOxygenOutputDirections() { return EnumSet.noneOf(EnumFacing.class); } public static HashMap<BlockVec3, TileEntityOxygenSealer> getSealersAround(World world, BlockPos pos, int rSquared) { HashMap<BlockVec3, TileEntityOxygenSealer> ret = new HashMap<BlockVec3, TileEntityOxygenSealer>(); for (TileEntityOxygenSealer tile : new ArrayList<TileEntityOxygenSealer>(TileEntityOxygenSealer.loadedTiles)) { if (tile != null && tile.getWorld() == world && tile.getDistanceSq(pos.getX(), pos.getY(), pos.getZ()) < rSquared) { ret.put(new BlockVec3(tile.getPos()), tile); } } return ret; } public static TileEntityOxygenSealer getNearestSealer(World world, double x, double y, double z) { TileEntityOxygenSealer ret = null; double dist = 96 * 96D; for (Object tile : world.loadedTileEntityList) { if (tile instanceof TileEntityOxygenSealer) { double testDist = ((TileEntityOxygenSealer) tile).getDistanceSq(x, y, z); if (testDist < dist) { dist = testDist; ret = (TileEntityOxygenSealer) tile; } } } return ret; } }