package net.glowstone.generator.objects.trees; import net.glowstone.util.BlockStateDelegate; import org.bukkit.DirtType; import org.bukkit.Location; import org.bukkit.Material; import org.bukkit.block.BlockFace; import org.bukkit.block.BlockState; import org.bukkit.material.Dirt; import java.util.Arrays; import java.util.Collection; import java.util.Random; public class GenericTree { protected final Random random; protected final Location loc; protected int height; protected int logType; protected int leavesType; protected final BlockStateDelegate delegate; protected Collection<Material> overridables; public GenericTree(Random random, Location location, BlockStateDelegate delegate) { this.random = random; this.loc = location; this.delegate = delegate; setOverridables( Material.AIR, Material.LEAVES, Material.GRASS, Material.DIRT, Material.LOG, Material.LOG_2, Material.SAPLING, Material.VINE ); setHeight(random.nextInt(3) + 4); setTypes(0, 0); } protected final void setOverridables(Material... overridables) { this.overridables = Arrays.asList(overridables); } protected final void setHeight(int height) { this.height = height; } protected final void setTypes(int logType, int leavesType) { this.logType = logType; this.leavesType = leavesType; } public boolean canHeightFit() { return loc.getBlockY() >= 1 && loc.getBlockY() + height + 1 <= 255; } public boolean canPlaceOn() { final BlockState state = delegate.getBlockState(loc.getBlock().getRelative(BlockFace.DOWN).getLocation()); return state.getType() == Material.GRASS || state.getType() == Material.DIRT || state.getType() == Material.SOIL; } public boolean canPlace() { for (int y = loc.getBlockY(); y <= loc.getBlockY() + 1 + height; y++) { // Space requirement int radius = 1; // default radius if above first block if (y == loc.getBlockY()) { radius = 0; // radius at source block y is 0 (only trunk) } else if (y >= loc.getBlockY() + 1 + height - 2) { radius = 2; // max radius starting at leaves bottom } // check for block collision on horizontal slices for (int x = loc.getBlockX() - radius; x <= loc.getBlockX() + radius; x++) { for (int z = loc.getBlockZ() - radius; z <= loc.getBlockZ() + radius; z++) { if (y >= 0 && y < 256) { // we can overlap some blocks around final Material type = delegate.getBlockState(loc.getWorld(), x, y, z).getType(); if (!overridables.contains(type)) { return false; } } else { // height out of range return false; } } } } return true; } public boolean generate() { if (!canHeightFit() || !canPlaceOn() || !canPlace()) { return false; } // generate the leaves for (int y = loc.getBlockY() + height - 3; y <= loc.getBlockY() + height; y++) { int n = y - (loc.getBlockY() + height); int radius = 1 - n / 2; for (int x = loc.getBlockX() - radius; x <= loc.getBlockX() + radius; x++) { for (int z = loc.getBlockZ() - radius; z <= loc.getBlockZ() + radius; z++) { if (Math.abs(x - loc.getBlockX()) != radius || Math.abs(z - loc.getBlockZ()) != radius || (random.nextBoolean() && n != 0)) { final Material material = delegate.getBlockState(loc.getWorld(), x, y, z).getType(); if (material == Material.AIR || material == Material.LEAVES) { delegate.setTypeAndRawData(loc.getWorld(), x, y, z, Material.LEAVES, leavesType); } } } } } // generate the trunk for (int y = 0; y < height; y++) { final Material material = delegate.getBlockState(loc.getWorld(), loc.getBlockX(), loc.getBlockY() + y, loc.getBlockZ()).getType(); if (material == Material.AIR || material == Material.LEAVES) { delegate.setTypeAndRawData(loc.getWorld(), loc.getBlockX(), loc.getBlockY() + y, loc.getBlockZ(), Material.LOG, logType); } } // block below trunk is always dirt final Dirt dirt = new Dirt(DirtType.NORMAL); delegate.setTypeAndData(loc.getWorld(), loc.getBlockX(), loc.getBlockY() - 1, loc.getBlockZ(), Material.DIRT, dirt); return true; } }