package micdoodle8.mods.galacticraft.core.tile; import io.netty.buffer.ByteBuf; import micdoodle8.mods.galacticraft.api.block.IOxygenReliantBlock; import micdoodle8.mods.galacticraft.api.item.IItemOxygenSupply; import micdoodle8.mods.galacticraft.api.vector.BlockVec3; import micdoodle8.mods.galacticraft.api.vector.BlockVec3Dim; import micdoodle8.mods.galacticraft.core.Constants; import micdoodle8.mods.galacticraft.core.blocks.BlockOxygenDistributor; import micdoodle8.mods.galacticraft.core.energy.item.ItemElectricBase; import micdoodle8.mods.galacticraft.core.entities.IBubbleProvider; import micdoodle8.mods.galacticraft.core.inventory.IInventoryDefaults; 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.block.state.IBlockState; 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.server.MinecraftServer; import net.minecraft.util.*; import net.minecraftforge.fml.relauncher.Side; import net.minecraftforge.fml.relauncher.SideOnly; import java.util.EnumSet; import java.util.HashSet; import java.util.List; public class TileEntityOxygenDistributor extends TileEntityOxygen implements IInventoryDefaults, ISidedInventory, IBubbleProvider { public boolean active; public boolean lastActive; private ItemStack[] containingItems = new ItemStack[2]; public static HashSet<BlockVec3Dim> loadedTiles = new HashSet(); public float bubbleSize; @NetworkedField(targetSide = Side.CLIENT) public boolean shouldRenderBubble = true; public TileEntityOxygenDistributor() { super(6000, 8); // this.oxygenBubble = null; } @Override public void onLoad() { if (!this.worldObj.isRemote) TileEntityOxygenDistributor.loadedTiles.add(new BlockVec3Dim(this)); } @Override public void onChunkUnload() { TileEntityOxygenDistributor.loadedTiles.remove(new BlockVec3Dim(this)); super.onChunkUnload(); } @Override public void invalidate() { if (!this.worldObj.isRemote/* && this.oxygenBubble != null*/) { int bubbleR = MathHelper.ceiling_double_int(bubbleSize); int bubbleR2 = (int) (bubbleSize * bubbleSize); final int xMin = this.getPos().getX() - bubbleR; final int xMax = this.getPos().getX() + bubbleR; final int yMin = this.getPos().getY() - bubbleR; final int yMax = this.getPos().getY() + bubbleR; final int zMin = this.getPos().getZ() - bubbleR; final int zMax = this.getPos().getZ() + bubbleR; for (int x = xMin; x < xMax; x++) { for (int z = zMin; z < zMax; z++) { //Doing y as the inner loop allows BlockVec3 to cache the chunks more efficiently for (int y = yMin; y < yMax; y++) { Block block = new BlockVec3(x, y, z).getBlockID(this.worldObj); if (block instanceof IOxygenReliantBlock && this.getDistanceFromServer(x, y, z) <= bubbleR2) { this.worldObj.scheduleUpdate(new BlockPos(x, y, z), block, 0); } } } } // this.oxygenBubble.setDead(); TileEntityOxygenDistributor.loadedTiles.remove(new BlockVec3Dim(this)); } super.invalidate(); } @Override public double getPacketRange() { return 64.0F; } @Override public void addExtraNetworkedData(List<Object> networkedList) { if (!this.worldObj.isRemote && !this.isInvalid()) { // networkedList.add(this.oxygenBubble != null); // if (this.oxygenBubble != null) // { // networkedList.add(this.oxygenBubble.getEntityId()); // } if (MinecraftServer.getServer().isDedicatedServer()) { networkedList.add(loadedTiles.size()); //TODO: Limit this to ones in the same dimension as this tile? for (BlockVec3Dim distributor : loadedTiles) { if (distributor == null) { networkedList.add(-1); networkedList.add(-1); networkedList.add(-1); networkedList.add(-1); } else { networkedList.add(distributor.x); networkedList.add(distributor.y); networkedList.add(distributor.z); networkedList.add(distributor.dim); } } } else //Signal integrated server, do not clear loadedTiles { networkedList.add(-1); } networkedList.add(this.bubbleSize); } } @Override @SideOnly(Side.CLIENT) public AxisAlignedBB getRenderBoundingBox() { return AxisAlignedBB.fromBounds(this.getPos().getX() - this.bubbleSize, this.getPos().getY() - this.bubbleSize, this.getPos().getZ() - this.bubbleSize, this.getPos().getX() + this.bubbleSize, this.getPos().getY() + this.bubbleSize, this.getPos().getZ() + this.bubbleSize); } @Override @SideOnly(Side.CLIENT) public double getMaxRenderDistanceSquared() { return Constants.RENDERDISTANCE_LONG; } @Override public void readExtraNetworkedData(ByteBuf dataStream) { if (this.worldObj.isRemote) { // if (dataStream.readBoolean()) // { // this.oxygenBubble = (EntityBubble) worldObj.getEntityByID(dataStream.readInt()); // } int size = dataStream.readInt(); if (size >= 0) { loadedTiles.clear(); for (int i = 0; i < size; ++i) { int i1 = dataStream.readInt(); int i2 = dataStream.readInt(); int i3 = dataStream.readInt(); int i4 = dataStream.readInt(); if (i1 == -1 && i2 == -1 && i3 == -1 && i4 == -1) { continue; } loadedTiles.add(new BlockVec3Dim(i1, i2, i3, i4)); } } this.bubbleSize = dataStream.readFloat(); } } public int getDistanceFromServer(int par1, int par3, int par5) { final int d3 = this.getPos().getX() - par1; final int d4 = this.getPos().getY() - par3; final int d5 = this.getPos().getZ() - par5; return d3 * d3 + d4 * d4 + d5 * d5; } @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(this.oxygenPerTick * 2.5F, this.getMaxOxygenStored() - this.getOxygenStored())); this.setOxygenStored(getOxygenStored() + oxygenItem.discharge(oxygenItemStack, oxygenDraw)); if (this.getOxygenStored() > this.getMaxOxygenStored()) { this.setOxygenStored(this.getOxygenStored()); } } } super.update(); if (!this.worldObj.isRemote) { if (this.getEnergyStoredGC() > 0.0F && this.hasEnoughEnergyToRun && this.getOxygenStored() > this.oxygenPerTick) { this.bubbleSize += 0.01F; } else { this.bubbleSize -= 0.1F; } this.bubbleSize = Math.min(Math.max(this.bubbleSize, 0.0F), 10.0F); } // if (!hasValidBubble && !this.worldObj.isRemote && (this.oxygenBubble == null || this.ticks < 25)) // { // //Check it's a world without a breathable atmosphere // if (this.oxygenBubble == null && this.worldObj.provider instanceof IGalacticraftWorldProvider && !((IGalacticraftWorldProvider)this.worldObj.provider).hasBreathableAtmosphere()) // { // this.oxygenBubble = new EntityBubble(this.worldObj, new Vector3(this), this); // this.hasValidBubble = true; // this.worldObj.spawnEntityInWorld(this.oxygenBubble); // } // } if (!this.worldObj.isRemote/* && this.oxygenBubble != null*/) { this.active = bubbleSize >= 1 && this.hasEnoughEnergyToRun && this.getOxygenStored() > this.oxygenPerTick; if (this.ticks % (this.active ? 20 : 4) == 0) { double size = bubbleSize; int bubbleR = MathHelper.floor_double(size) + 4; int bubbleR2 = (int) (size * size); for (int x = this.getPos().getX() - bubbleR; x <= this.getPos().getX() + bubbleR; x++) { for (int y = this.getPos().getY() - bubbleR; y <= this.getPos().getY() + bubbleR; y++) { for (int z = this.getPos().getZ() - bubbleR; z <= this.getPos().getZ() + bubbleR; z++) { BlockPos pos = new BlockPos(x, y, z); IBlockState state = this.worldObj.getBlockState(pos); Block block = state.getBlock(); if (block instanceof IOxygenReliantBlock) { if (this.getDistanceFromServer(x, y, z) <= bubbleR2) { ((IOxygenReliantBlock) block).onOxygenAdded(this.worldObj, pos, state); } else { //Do not necessarily extinguish it - it might be inside another oxygen system this.worldObj.scheduleUpdate(pos, block, 0); } } } } } } } this.lastActive = this.active; } @Override public void readFromNBT(NBTTagCompound nbt) { super.readFromNBT(nbt); if (nbt.hasKey("bubbleVisible")) { this.setBubbleVisible(nbt.getBoolean("bubbleVisible")); } if (nbt.hasKey("bubbleSize")) { this.bubbleSize = nbt.getFloat("bubbleSize"); } // this.hasValidBubble = nbt.getBoolean("hasValidBubble"); final NBTTagList var2 = nbt.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 nbt) { super.writeToNBT(nbt); nbt.setBoolean("bubbleVisible", this.shouldRenderBubble); nbt.setFloat("bubbleSize", this.bubbleSize); // nbt.setBoolean("hasValidBubble", this.hasValidBubble); 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); } } nbt.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.oxygendistributor.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(); 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; } return false; } @Override public boolean shouldUseEnergy() { return this.getOxygenStored() > this.oxygenPerTick; } @Override public EnumFacing getFront() { return this.worldObj.getBlockState(getPos()).getValue(BlockOxygenDistributor.FACING); } @Override public EnumFacing getElectricInputDirection() { return getFront().rotateY(); } @Override public ItemStack getBatteryInSlot() { return this.getStackInSlot(0); } @Override public boolean shouldUseOxygen() { return this.hasEnoughEnergyToRun; } @Override public EnumSet<EnumFacing> getOxygenInputDirections() { return EnumSet.of(this.getElectricInputDirection().getOpposite()); } @Override public EnumSet<EnumFacing> getOxygenOutputDirections() { return EnumSet.noneOf(EnumFacing.class); } // @Override // public IBubble getBubble() // { // return this.oxygenBubble; // } public boolean inBubble(double pX, double pY, double pZ) { double r = bubbleSize; r *= r; double d3 = this.getPos().getX() + 0.5D - pX; d3 *= d3; if (d3 > r) { return false; } double d4 = this.getPos().getZ() + 0.5D - pZ; d4 *= d4; if (d3 + d4 > r) { return false; } double d5 = this.getPos().getY() + 0.5D - pY; return d3 + d4 + d5 * d5 < r; } @Override public void setBubbleVisible(boolean shouldRender) { this.shouldRenderBubble = shouldRender; // this.oxygenBubble.setShouldRender(shouldRender); } @Override public float getBubbleSize() { return this.bubbleSize; } @Override public boolean getBubbleVisible() { return this.shouldRenderBubble; } }