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.LeavesDecayEvent;
import org.bukkit.inventory.ItemStack;
import org.bukkit.util.Vector;
import java.util.*;
public class BlockLeaves extends BlockType {
private final byte[] blockMap = new byte[11 * 11 * 11];
@Override
public void placeBlock(GlowPlayer player, GlowBlockState state, BlockFace face, ItemStack holding, Vector clickedLoc) {
super.placeBlock(player, state, face, holding, clickedLoc);
state.setRawData((byte) (state.getRawData() | 0x04));
}
@Override
public Collection<ItemStack> getDrops(GlowBlock block, ItemStack tool) {
int data = block.getData() & 0x03; //ignore "non-decay" and "check-decay" data.
if (tool != null && tool.getType() == Material.SHEARS) {
return Collections.unmodifiableList(Arrays.asList(
new ItemStack(block.getType(), 1, (short) data)
));
}
if (block.getType() == Material.LEAVES_2) {
data += 4;
}
List<ItemStack> drops = new ArrayList<>();
if (random.nextFloat() < (block.getData() == 3 ? .025f : .05f)) { //jungle leaves drop with 2.5% chance, others drop with 5%
drops.add(new ItemStack(Material.SAPLING, 1, (short) data));
}
if (data == 0 && random.nextFloat() < .005) { //oak leaves have a .5% chance to drop an apple
drops.add(new ItemStack(Material.APPLE));
}
return Collections.unmodifiableList(drops);
}
@Override
public boolean canTickRandomly() {
return true;
}
@Override
public void blockDestroy(GlowPlayer player, GlowBlock block, BlockFace face) {
// vanilla set decay check in a 3x3x3 neighboring when a leaves block is removed
final GlowWorld world = block.getWorld();
for (int x = 0; x < 3; x++) {
for (int z = 0; z < 3; z++) {
for (int y = 0; y < 3; y++) {
final GlowBlock b = world.getBlockAt(block.getLocation().add(x - 1, y - 1, z - 1));
if (b.getType() == Material.LEAVES || b.getType() == Material.LEAVES_2) {
final GlowBlockState state = b.getState();
if ((state.getRawData() & 0x08) == 0 && (state.getRawData() & 0x04) == 0) { // check decay is off and decay is on
// set decay check on for this leaves block
state.setRawData((byte) (state.getRawData() | 0x08));
state.update(true);
}
}
}
}
}
}
@Override
public void updateBlock(GlowBlock block) {
final GlowBlockState state = block.getState();
if ((state.getRawData() & 0x08) != 0 && (state.getRawData() & 0x04) == 0) { // check decay is on and decay is on
final GlowWorld world = block.getWorld();
// build a 9x9x9 box to map neighboring blocks
for (int x = 0; x < 9; x++) {
for (int z = 0; z < 9; z++) {
for (int y = 0; y < 9; y++) {
final GlowBlock b = world.getBlockAt(block.getLocation().add(x - 4, y - 4, z - 4));
byte val = 127;
if (b.getType() == Material.LOG || b.getType() == Material.LOG_2) {
val = 0;
} else if (b.getType() == Material.LEAVES || b.getType() == Material.LEAVES_2) {
val = -1;
}
setBlockInMap(val, x, y, z);
}
}
}
// browse the map in several pass to detect connected leaves:
// leaf block that is 5 blocks away from log or without connection
// to another connected leaves block will decay
for (int i = 0; i < 4; i++) {
for (int x = 0; x < 9; x++) {
for (int z = 0; z < 9; z++) {
for (int y = 0; y < 9; y++) {
if (getBlockInMap(x, y, z) == i) {
if (getBlockInMap(x - 1, y, z) == -1) {
setBlockInMap((byte) (i + 1), x - 1, y, z);
}
if (getBlockInMap(x, y - 1, z) == -1) {
setBlockInMap((byte) (i + 1), x, y - 1, z);
}
if (getBlockInMap(x, y, z - 1) == -1) {
setBlockInMap((byte) (i + 1), x, y, z - 1);
}
if (getBlockInMap(x + 1, y, z) == -1) {
setBlockInMap((byte) (i + 1), x + 1, y, z);
}
if (getBlockInMap(x, y + 1, z) == -1) {
setBlockInMap((byte) (i + 1), x, y + 1, z);
}
if (getBlockInMap(x, y, z + 1) == -1) {
setBlockInMap((byte) (i + 1), x, y, z + 1);
}
}
}
}
}
}
if (getBlockInMap(4, 4, 4) < 0) { // leaf decay
LeavesDecayEvent decayEvent = new LeavesDecayEvent(block);
EventFactory.callEvent(decayEvent);
if (!decayEvent.isCancelled()) {
block.breakNaturally();
}
} else { // cancel decay check on this leaves block
state.setRawData((byte) (state.getRawData() & -0x09));
state.update(true);
}
}
}
private byte getBlockInMap(int x, int y, int z) {
return blockMap[((x + 1) * 11 + (z + 1)) * 11 + (y + 1)];
}
private void setBlockInMap(byte val, int x, int y, int z) {
blockMap[((x + 1) * 11 + (z + 1)) * 11 + (y + 1)] = val;
}
}