package net.glowstone.block.blocktype;

import net.glowstone.block.GlowBlock;
import org.bukkit.Material;
import org.bukkit.block.BlockFace;

public class BlockCactus extends BlockType {

    private static final BlockFace[] NEAR_BLOCKS = new BlockFace[]{BlockFace.NORTH, BlockFace.SOUTH, BlockFace.WEST, BlockFace.EAST};

    @Override
    public boolean canPlaceAt(GlowBlock block, BlockFace against) {
        Material below = block.getRelative(BlockFace.DOWN).getType();
        return (below == Material.CACTUS || below == Material.SAND) && !hasNearBlocks(block);
    }

    @Override
    public void onNearBlockChanged(GlowBlock block, BlockFace face, GlowBlock changedBlock, Material oldType, byte oldData, Material newType, byte newData) {
        updatePhysics(block);
    }

    @Override
    public void updatePhysics(GlowBlock me) {
        if (!canPlaceAt(me, BlockFace.DOWN)) {
            me.breakNaturally();
        }
    }

    private boolean hasNearBlocks(GlowBlock block) {
        for (BlockFace face : NEAR_BLOCKS) {
            if (!canPlaceNear(block.getRelative(face).getType())) {
                return true;
            }
        }
        return false;
    }

    private boolean canPlaceNear(Material type) {
        // TODO: return true for non-buildable blocks
        switch (type) {
            case AIR:
            case WATER:
            case STATIONARY_WATER:
            case LAVA:
            case STATIONARY_LAVA:
            case PORTAL:
            case ENDER_PORTAL:
            case RED_ROSE:
            case YELLOW_FLOWER:
                return true;
        }
        return false;
    }
}

@@ -58,86 +58,241 @@
 one.
-block.GlowBlock;%0Aimport org.bukkit.Material;%0Aimport org.bukkit.block.BlockFace
+EventFactory;%0Aimport net.glowstone.block.GlowBlock;%0Aimport net.glowstone.block.GlowBlockState;%0Aimport org.bukkit.Material;%0Aimport org.bukkit.block.Block;%0Aimport org.bukkit.block.BlockFace;%0Aimport org.bukkit.event.block.BlockGrowEvent
 ;%0A%0Ap
@@ -1082,32 +1082,1600 @@
        %7D%0A    %7D%0A%0A
+    @Override%0A    public boolean canTickRandomly() %7B%0A        return true;%0A    %7D%0A%0A    @Override%0A    public void updateBlock(GlowBlock block) %7B%0A        final GlowBlock blockAbove = block.getRelative(BlockFace.UP);%0A        // check it's the highest block of cactus%0A        if (blockAbove.isEmpty()) %7B%0A            // check the current cactus height%0A            Block blockBelow = block.getRelative(BlockFace.DOWN);%0A            int height = 1;%0A            while (blockBelow.getType() == Material.CACTUS) %7B%0A                height++;%0A                blockBelow = blockBelow.getRelative(BlockFace.DOWN);%0A            %7D%0A            if (height %3C 3) %7B%0A                GlowBlockState state = block.getState();%0A                if (state.getRawData() %3C 15) %7B%0A                    // increase age%0A                    state.setRawData((byte) (state.getRawData() + 1));%0A                    state.update(true);%0A                %7D else %7B%0A                    // grow the cactus on the above block%0A                    state.setRawData((byte) 0);%0A                    state.update(true);%0A                    state = blockAbove.getState();%0A                    state.setType(Material.CACTUS);%0A                    state.setRawData((byte) 0);%0A                    BlockGrowEvent growEvent = new BlockGrowEvent(blockAbove, state);%0A                    EventFactory.callEvent(growEvent);%0A                    if (!growEvent.isCancelled()) %7B%0A                        state.update(true);%0A                    %7D%0A                    updatePhysics(blockAbove);%0A                %7D%0A            %7D%0A        %7D%0A    %7D%0A%0A
     private bool
@@ -3320,34 +3320,48 @@
 ue;%0A        
-%7D%0A
+    default:%0A   
         
 return false
@@ -3340,32 +3340,37 @@
 ult:%0A           
+     
 return false;%0A  
@@ -3363,20 +3363,30 @@
 turn false;%0A
+        %7D%0A
     %7D%0A%7D%0A

Add cactus growth