package blusunrize.immersiveengineering.common.blocks.metal; import blusunrize.immersiveengineering.api.IEEnums.SideConfig; import blusunrize.immersiveengineering.api.Lib; import blusunrize.immersiveengineering.common.Config.IEConfig; import blusunrize.immersiveengineering.common.blocks.IEBlockInterfaces.IBlockOverlayText; import blusunrize.immersiveengineering.common.blocks.IEBlockInterfaces.IConfigurableSides; import blusunrize.immersiveengineering.common.blocks.TileEntityIEBase; import net.minecraft.block.BlockLiquid; import net.minecraft.block.state.IBlockState; import net.minecraft.client.resources.I18n; import net.minecraft.entity.player.EntityPlayer; import net.minecraft.nbt.NBTTagCompound; import net.minecraft.util.EnumFacing; import net.minecraft.util.ITickable; import net.minecraft.util.math.BlockPos; import net.minecraft.util.math.RayTraceResult; import net.minecraftforge.common.capabilities.Capability; import net.minecraftforge.fluids.*; import net.minecraftforge.fluids.capability.CapabilityFluidHandler; import javax.annotation.Nullable; import java.util.*; public class TileEntityFluidPlacer extends TileEntityIEBase implements ITickable, IConfigurableSides, IBlockOverlayText { public int[] sideConfig = new int[] {1,0,1,1,1,1}; public FluidTank tank = new FluidTank(4000); private int tickCount = 0; HashSet<BlockPos> checkedPositions = new HashSet<BlockPos>(); TreeMap<Integer, Queue<BlockPos>> layeredPlacementQueue = new TreeMap<Integer, Queue<BlockPos>>(); HashSet<BlockPos> tempFluids = new HashSet<BlockPos>(); @Override public void update() { if(getWorld().isRemote || getWorld().isBlockIndirectlyGettingPowered(getPos())!=0) return; if(tickCount%16==0) { if(tickCount%512==0)//Initial placement prepareAreaCheck(); if(tank.getFluidAmount()>=Fluid.BUCKET_VOLUME && tank.getFluid().getFluid().getBlock()!=null && !layeredPlacementQueue.isEmpty()) { Queue<BlockPos> lowestLayer = layeredPlacementQueue.firstEntry().getValue(); if(lowestLayer==null || lowestLayer.isEmpty()) layeredPlacementQueue.pollFirstEntry(); else { BlockPos targetPos = lowestLayer.poll(); IBlockState state = getWorld().getBlockState(targetPos); if((state.getBlock().isAir(state,getWorld(),targetPos) || !state.getMaterial().isSolid()) && !isFullFluidBlock(targetPos, state)) if(FluidUtil.tryPlaceFluid(null, getWorld(), tank.getFluid(), targetPos)) { tank.drain(Fluid.BUCKET_VOLUME, true); addConnectedSpaces(targetPos); handleTempFluids(); } } } } tickCount++; } private void prepareAreaCheck() { checkedPositions.clear(); layeredPlacementQueue.clear(); tempFluids.clear(); addConnectedSpaces(getPos()); handleTempFluids(); } private Queue<BlockPos> getQueueForYLevel(int yLevel) { Queue<BlockPos> queue = layeredPlacementQueue.get(yLevel); if(queue==null) { queue = new LinkedList<BlockPos>(); layeredPlacementQueue.put(yLevel, queue); } return queue; } private void addConnectedSpaces(BlockPos pos) { for(EnumFacing facing : EnumFacing.values()) if(facing!=EnumFacing.UP && (pos!=getPos()||sideConfig[facing.ordinal()]==1)) addToQueue(pos.offset(facing)); } private void addToQueue(BlockPos pos) { if(pos.getY()>=0 && pos.getY()<=255)//Within world borders if(checkedPositions.add(pos))//Don't add checked positions if(pos.distanceSq(getPos())<64*64)//Within max range if(getWorld().isBlockLoaded(pos)) { IBlockState state = getWorld().getBlockState(pos); if(tank.getFluid()!=null && tank.getFluid().getFluid()==FluidRegistry.lookupFluidForBlock(state.getBlock())) tempFluids.add(pos); if((state.getBlock().isAir(state,getWorld(),pos) || !state.getMaterial().isSolid()) && !isFullFluidBlock(pos, state)) getQueueForYLevel(pos.getY()).add(pos); } } private void handleTempFluids() { Set<BlockPos> tempFluidsCopy = tempFluids;//preventing CMEs >_> tempFluids = new HashSet<>(); for(BlockPos pos : tempFluidsCopy) addConnectedSpaces(pos); } private boolean isFullFluidBlock(BlockPos pos, IBlockState state) { if(state.getBlock() instanceof IFluidBlock) return Math.abs(((IFluidBlock)state.getBlock()).getFilledPercentage(getWorld(),pos))==1; else if(state.getBlock() instanceof BlockLiquid) return state.getBlock().getMetaFromState(state)==0; return false; } @Override public void readCustomNBT(NBTTagCompound nbt, boolean descPacket) { sideConfig = nbt.getIntArray("sideConfig"); if(sideConfig==null || sideConfig.length!=6) sideConfig = new int[]{1,0,1,1,1,1}; tank.readFromNBT(nbt.getCompoundTag("tank")); if(descPacket) this.markContainingBlockForUpdate(null); } @Override public void writeCustomNBT(NBTTagCompound nbt, boolean descPacket) { nbt.setIntArray("sideConfig", sideConfig); nbt.setTag("tank", tank.writeToNBT(new NBTTagCompound())); } @Override public SideConfig getSideConfig(int side) { return (side>=0&&side<6)?SideConfig.values()[this.sideConfig[side]+1]: SideConfig.NONE; } @Override public boolean toggleSide(int side, EntityPlayer p) { sideConfig[side]++; if(sideConfig[side]>1) sideConfig[side]=-1; this.markDirty(); this.markContainingBlockForUpdate(null); getWorld().addBlockEvent(getPos(), this.getBlockType(), 0, 0); return true; } @Override public boolean hasCapability(Capability<?> capability, @Nullable EnumFacing facing) { if(capability == CapabilityFluidHandler.FLUID_HANDLER_CAPABILITY && (facing==null||sideConfig[facing.ordinal()]==0) ) return true; return super.hasCapability(capability, facing); } @Override public <T> T getCapability(Capability<T> capability, @Nullable EnumFacing facing) { if(capability == CapabilityFluidHandler.FLUID_HANDLER_CAPABILITY && (facing==null||sideConfig[facing.ordinal()]==0) ) return (T)tank; return super.getCapability(capability, facing); } @Override public String[] getOverlayText(EntityPlayer player, RayTraceResult mop, boolean hammer) { if(hammer && IEConfig.colourblindSupport) { int i = sideConfig[Math.min(sideConfig.length-1, mop.sideHit.ordinal())]; int j = sideConfig[Math.min(sideConfig.length-1, mop.sideHit.getOpposite().ordinal())]; return new String[]{ I18n.format(Lib.DESC_INFO+"blockSide.facing") +": "+ I18n.format(Lib.DESC_INFO+"blockSide.connectFluid."+i), I18n.format(Lib.DESC_INFO+"blockSide.opposite") +": "+ I18n.format(Lib.DESC_INFO+"blockSide.connectFluid."+j) }; } return null; } @Override public boolean useNixieFont(EntityPlayer player, RayTraceResult mop) { return false; } }