package net.glowstone.block.blocktype; import net.glowstone.EventFactory; import net.glowstone.GlowWorld; import net.glowstone.block.GlowBlock; import net.glowstone.block.GlowBlockState; import net.glowstone.entity.GlowPlayer; import org.bukkit.Material; import org.bukkit.block.BlockFace; import org.bukkit.event.block.BlockSpreadEvent; import org.bukkit.inventory.ItemStack; import org.bukkit.material.MaterialData; import org.bukkit.material.Vine; import org.bukkit.util.Vector; import java.util.Arrays; import java.util.Collection; public class BlockVine extends BlockClimbable { private static final BlockFace[] FACES = {BlockFace.NORTH, BlockFace.SOUTH, BlockFace.EAST, BlockFace.WEST, BlockFace.DOWN, BlockFace.UP}; private static final BlockFace[] HORIZONTAL_FACES = {BlockFace.NORTH, BlockFace.SOUTH, BlockFace.EAST, BlockFace.WEST}; @Override public boolean canPlaceAt(GlowBlock block, BlockFace against) { switch (against) { case NORTH: case SOUTH: case EAST: case WEST: return block.getRelative(against.getOppositeFace()).getType().isSolid(); case UP: return block.getRelative(against).getType().isSolid(); default: return false; } } @Override public void placeBlock(GlowPlayer player, GlowBlockState state, BlockFace face, ItemStack holding, Vector clickedLoc) { super.placeBlock(player, state, face, holding, clickedLoc); MaterialData data = state.getData(); if (data instanceof Vine) { if (face == BlockFace.DOWN || face == BlockFace.UP) { return; } else { ((Vine) data).putOnFace(face.getOppositeFace()); } state.setData(data); } else { warnMaterialData(Vine.class, data); } } @Override public boolean canAbsorb(GlowBlock block, BlockFace face, ItemStack holding) { return true; } @Override public boolean canOverride(GlowBlock block, BlockFace face, ItemStack holding) { return true; } @Override public Collection<ItemStack> getDrops(GlowBlock block, ItemStack tool) { if (tool != null && tool.getType() == Material.SHEARS) return Arrays.asList(new ItemStack(Material.VINE)); return BlockDropless.EMPTY_STACK; } @Override public boolean canTickRandomly() { return true; } @Override public void updateBlock(GlowBlock block) { if (random.nextInt(4) == 0) { final GlowBlockState state = block.getState(); final MaterialData data = state.getData(); if (data instanceof Vine) { final Vine vine = (Vine) data; final boolean hasNearVineBlocks = hasNearVineBlocks(block); final BlockFace face = FACES[random.nextInt(FACES.length)]; if (block.getY() < 255 && face == BlockFace.UP && block.getRelative(face).isEmpty()) { if (!hasNearVineBlocks) { final Vine v = (Vine) data; for (BlockFace f : HORIZONTAL_FACES) { if (random.nextInt(2) == 0 || !block.getRelative(f).getRelative(face).getType().isSolid()) { v.removeFromFace(f); } } putVineOnHorizontalBlockFace(block.getRelative(face), v, block); } } else if (Arrays.asList(HORIZONTAL_FACES).contains(face) && !vine.isOnFace(face)) { if (!hasNearVineBlocks) { final GlowBlock b = block.getRelative(face); if (b.isEmpty()) { final BlockFace fcw = getClockwiseFace(face); final BlockFace fccw = getCounterClockwiseFace(face); final GlowBlock bcw = b.getRelative(fcw); final GlowBlock bccw = b.getRelative(fccw); final boolean isOnCWFace = vine.isOnFace(fcw); final boolean isOnCCWFace = vine.isOnFace(fccw); if (isOnCWFace && bcw.getType().isSolid()) { putVine(b, new Vine(fcw), block); } else if (isOnCCWFace && bccw.getType().isSolid()) { putVine(b, new Vine(fccw), block); } else if (isOnCWFace && bcw.isEmpty() && block.getRelative(fcw).getType().isSolid()) { putVine(bcw, new Vine(face.getOppositeFace()), block); } else if (isOnCCWFace && bccw.isEmpty() && block.getRelative(fccw).getType().isSolid()) { putVine(bccw, new Vine(face.getOppositeFace()), block); } else if (b.getRelative(BlockFace.UP).getType().isSolid()) { putVine(b, new Vine(), block); } } else if (b.getType().isOccluding()) { vine.putOnFace(face); putVine(block, vine, null); } } } else if (block.getY() > 1) { final GlowBlock b = block.getRelative(BlockFace.DOWN); final Vine v = (Vine) data; if (b.getType() == Material.VINE || b.isEmpty()) { for (BlockFace f : HORIZONTAL_FACES) { if (random.nextInt(2) == 0) { v.removeFromFace(f); } } putVineOnHorizontalBlockFace(b, v, b.isEmpty() ? block : null); } } } else { warnMaterialData(Vine.class, data); } } } private static BlockFace getClockwiseFace(BlockFace face) { switch (face) { case NORTH: return BlockFace.EAST; case SOUTH: return BlockFace.WEST; case EAST: return BlockFace.SOUTH; case WEST: return BlockFace.NORTH; default: return BlockFace.NORTH; } } private static BlockFace getCounterClockwiseFace(BlockFace face) { switch (face) { case NORTH: return BlockFace.WEST; case SOUTH: return BlockFace.EAST; case EAST: return BlockFace.NORTH; case WEST: return BlockFace.SOUTH; default: return BlockFace.NORTH; } } private void putVine(GlowBlock block, Vine vine, GlowBlock fromBlock) { final GlowBlockState state = block.getState(); state.setType(Material.VINE); state.setData(vine); if (fromBlock != null) { BlockSpreadEvent spreadEvent = new BlockSpreadEvent(block, fromBlock, state); EventFactory.callEvent(spreadEvent); if (!spreadEvent.isCancelled()) { state.update(true); } } else { state.update(true); } } private void putVineOnHorizontalBlockFace(GlowBlock block, Vine vine, GlowBlock fromBlock) { boolean isOnHorizontalFace = false; for (BlockFace f : HORIZONTAL_FACES) { if (vine.isOnFace(f)) { isOnHorizontalFace = true; break; } } if (isOnHorizontalFace) { putVine(block, vine, fromBlock); } } private boolean hasNearVineBlocks(GlowBlock block) { final GlowWorld world = block.getWorld(); int vineCount = 0; for (int x = 0; x < 9; x++) { for (int z = 0; z < 9; z++) { for (int y = 0; y < 3; y++) { if (world.getBlockAt(block.getLocation().add(x - 4, y - 1, z - 4)).getType() == Material.VINE) { if (++vineCount >= 5) { return true; } } } } } return false; } }