package com.bioxx.tfc2.blocks.terrain; import java.util.*; import net.minecraft.block.Block; import net.minecraft.block.material.Material; import net.minecraft.block.properties.PropertyHelper; import net.minecraft.block.state.IBlockState; import net.minecraft.util.EnumFacing; import net.minecraft.util.math.BlockPos; import net.minecraft.world.IBlockAccess; import net.minecraft.world.World; import com.bioxx.tfc2.api.interfaces.IGravityBlock; import com.bioxx.tfc2.blocks.BlockTerra; import com.bioxx.tfc2.entity.EntityFallingBlockTFC; public class BlockCollapsible extends BlockTerra { protected int scanDepth = 20; protected boolean compressionBreak = false; protected CollapsibleType collapseType = CollapsibleType.Structure; public BlockCollapsible(Material m, PropertyHelper p) { super(m, p); } /******************************************************************************* * 1. Content *******************************************************************************/ @Override public void onBlockAdded(World world, BlockPos pos, IBlockState state) { world.scheduleUpdate(pos, this, tickRate(world)); } @Override public void neighborChanged(IBlockState state, World world, BlockPos pos, Block blockIn) { world.scheduleUpdate(pos, this, tickRate(world)); } @Override public void updateTick(World world, BlockPos pos, IBlockState state, Random rand) { doCollapse(world, pos, state); } protected void doCollapse(World world, BlockPos pos, IBlockState state) { if (!world.isRemote) { state = state.getBlock().getActualState(state, world, pos); //If this block is considered natural then it uses the support scan for natural blocks if(collapseType == CollapsibleType.Nature && !hasSupport(world, pos, state)) { createFallingEntity(world, pos, state); scheduleNeighbors(world, pos); return; } if(collapseType == CollapsibleType.Structure && !hasSupport(world, pos, state)) { createFallingEntity(world, pos, state); scheduleNeighbors(world, pos); return; } } } public void createFallingEntity(World world, BlockPos pos, IBlockState state) { IBlockState stateNew = getFallBlockType(state); if(stateNew.getBlock() instanceof IGravityBlock) { world.setBlockToAir(pos); EntityFallingBlockTFC entityfallingblock = new EntityFallingBlockTFC(world, pos.getX() + 0.5D, pos.getY(), pos.getZ() + 0.5D, stateNew); ((IGravityBlock)getFallBlockType(state).getBlock()).onStartFalling(entityfallingblock); world.spawnEntityInWorld(entityfallingblock); onCreateFallingEntity(entityfallingblock, world, pos); } else { world.setBlockState(pos, getFallBlockType(state)); } } protected void onCreateFallingEntity(EntityFallingBlockTFC entity, World world, BlockPos pos) { } protected void scheduleNeighbors(World world, BlockPos pos) { for(int x = -1; x <= 1; x++) { for(int z = -1; z <= 1; z++) { for(int y = 0; y <= 2; y++) { world.scheduleUpdate(pos.add(x, y, z), world.getBlockState(pos.add(x, y, z)).getBlock(), tickRate(world)); } } } } protected boolean hasSupport(World world, BlockPos pos, IBlockState state) { IBlockState scanState, scanState2; Block scanBlock; BlockPos scanPos; BlockPos pos1 = new BlockPos(pos.getX()+0.5, 0, pos.getZ()+0.5), pos2; boolean isSupported = false; float supportRange = getNaturalSupportRange(world, pos, state); float supportRangeSq = supportRange*supportRange; //If the block beneath this one is solid then we will assume for now that this block is naturally supported if(world.getBlockState(pos.down()).getBlock().isBlockSolid(world, pos.down(), EnumFacing.UP)) return true; //If we fail the first case scenario then we will begin a recursive scan downward for each of the blocks surrounding this block Queue<BlockPos> scanQueue = new LinkedList<BlockPos>(); BlockPosList scannedQueue = new BlockPosList(); scannedQueue.add(pos); if(recievesHorizontalSupport(state, world, pos, EnumFacing.NORTH)) scanQueue.add(pos.north()); if(recievesHorizontalSupport(state, world, pos, EnumFacing.SOUTH)) scanQueue.add(pos.south()); if(recievesHorizontalSupport(state, world, pos, EnumFacing.EAST)) scanQueue.add(pos.east()); if(recievesHorizontalSupport(state, world, pos, EnumFacing.WEST)) scanQueue.add(pos.west()); while(scanQueue.peek() != null) { scanPos = scanQueue.poll(); scanState = world.getBlockState(scanPos); scanBlock = scanState.getBlock(); scanState = scanBlock.getActualState(scanState, world, scanPos); scannedQueue.add(scanPos); //If we've scanned down X blocks and we're still scanning then just assume that we're supported so we do not waste time if(pos.subtract(scanPos).getY() > scanDepth) { isSupported = true; break; } //If we move horizontally more than X blocks then stop scanning any further away pos2 = new BlockPos(scanPos.getX()+0.5, 0, scanPos.getZ()+0.5); double dist = pos1.distanceSq(pos2); if(dist > supportRangeSq) { continue; } else if(canBeSupportedBy(state, scanState)) { scanState2 = world.getBlockState(scanPos.down()); if(scanState2.getBlock().isSideSolid(scanState2, world, scanPos.down(), EnumFacing.UP)) { if(!scannedQueue.contains(scanPos.down())) scanQueue.add(scanPos.down()); } else { if(recievesHorizontalSupport(scanState, world, scanPos, EnumFacing.NORTH) && !scannedQueue.contains(scanPos.north())) scanQueue.add(scanPos.north()); if(recievesHorizontalSupport(scanState, world, scanPos, EnumFacing.SOUTH) && !scannedQueue.contains(scanPos.south())) scanQueue.add(scanPos.south()); if(recievesHorizontalSupport(scanState, world, scanPos, EnumFacing.EAST) && !scannedQueue.contains(scanPos.east())) scanQueue.add(scanPos.east()); if(recievesHorizontalSupport(scanState, world, scanPos,EnumFacing.WEST) && !scannedQueue.contains(scanPos.west())) scanQueue.add(scanPos.west()); } } } return isSupported; } /** * @return Can this block recieve support from the block in this direction. Usually false * if the neighboring block is not a full block or not a support block. */ public boolean recievesHorizontalSupport(IBlockState myState, IBlockAccess world, BlockPos pos, EnumFacing facing) { BlockPos otherPos = pos.offset(facing); IBlockState otherState = world.getBlockState(otherPos); if(otherState.getBlock().isAir(otherState, world, otherPos) || !otherState.getBlock().isSideSolid(otherState, world, otherPos, facing.getOpposite())) return false; //Both this block and the one in the facing direction need to have solid sides touching if(!myState.getBlock().isSideSolid(myState, world, pos, facing)) return false; return true; } public boolean canBeSupportedBy(IBlockState myState, IBlockState otherState) { if(otherState.getBlock() instanceof BlockCollapsible) return true; return false; } /** * @return What blockstate should this block turn into if it collapses */ public IBlockState getFallBlockType(IBlockState myState) { return myState; } public int getNaturalSupportRange(IBlockAccess world, BlockPos pos, IBlockState myState) { return 5; } /******************************************************************************* * 2. Rendering *******************************************************************************/ /******************************************************************************* * 3. Blockstate *******************************************************************************/ /** * * @author Bioxx * */ public static enum CollapsibleType { Nature, Structure; } /*** * Built this Custom List because BlockPos.equals as used in generic list types * did not seem to be properly comparing BlockPos Objects. * @author Bioxx * */ private static class BlockPosList extends AbstractList<BlockPos> { private static final int INITIAL_CAPACITY = 16; private BlockPos[] a; private int size = 0; BlockPosList() { a = new BlockPos[INITIAL_CAPACITY]; } @Override public BlockPos get(int index) { return a[index]; } @Override public BlockPos set(int index, BlockPos element) { BlockPos oldValue = a[index]; a[index] = element; return oldValue; } @Override public boolean add(BlockPos e) { if (size == a.length) { ensureCapacity(); //increase current capacity of list, make it double. } a[size++] = e; return true; } private void ensureCapacity() { int newIncreasedCapacity = a.length * 2; a = Arrays.copyOf(a, newIncreasedCapacity); } @Override public int size() { return a.length; } @Override public boolean contains(Object obj) { BlockPos compare = (BlockPos)obj; for(int i = 0; i < size; i++) { if(a[i] != null && a[i].getX() == compare.getX() && a[i].getY() == compare.getY() && a[i].getZ() == compare.getZ()) return true; } return false; } } }