/* * Copyright (C) 2013-2016 Gonçalo Baltazar <me@goncalomb.com> * * This file is part of NBTEditor. * * NBTEditor is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * NBTEditor is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with NBTEditor. If not, see <http://www.gnu.org/licenses/>. */ package com.goncalomb.bukkit.customitems.items; import java.util.ArrayList; import java.util.HashSet; import java.util.List; import java.util.Set; import org.bukkit.Material; import org.bukkit.block.Block; import org.bukkit.event.player.PlayerInteractEvent; import org.bukkit.potion.PotionEffect; import org.bukkit.potion.PotionEffectType; import com.goncalomb.bukkit.customitems.api.CustomItem; import com.goncalomb.bukkit.customitems.api.PlayerDetails; public abstract class GenericSuperAxe extends CustomItem { protected GenericSuperAxe(String slug, String name) { super(slug, name, Material.DIAMOND_AXE); } protected boolean isLog(Material mat) { return (mat == Material.LOG || mat == Material.LOG_2); } @Override public void onLeftClick(PlayerInteractEvent event, PlayerDetails details) { if (event.getAction() == org.bukkit.event.block.Action.LEFT_CLICK_BLOCK && isLog(event.getClickedBlock().getType())) { event.getPlayer().addPotionEffect(new PotionEffect(PotionEffectType.SLOW_DIGGING, 5*20, 3), true); } } protected Set<Block> getTreeBlocks(Block root) { return new BlockFinder(root).getBlocks(); } private final class BlockFinder { @SuppressWarnings("serial") private final class BlockLimitException extends Exception { } private Block _root; private HashSet<Block> _finalSet; private HashSet<Block> _leaves; private HashSet<Block> _groundedBlocks; public BlockFinder(Block root) { _root = root; } private void checkBlockLimit(int more) throws BlockLimitException { if (_finalSet.size() + more > 1000) { throw new BlockLimitException(); } } private boolean isLog(Material mat) { return (mat == Material.LOG || mat == Material.LOG_2); } private boolean isLeaves(Material mat) { return (mat == Material.LEAVES || mat == Material.LEAVES_2); } private boolean isGround(Material mat) { return (!isLog(mat) && !isLeaves(mat) && mat.isSolid()); } public Set<Block> getBlocks() { if (_finalSet == null) { try { _finalSet = new HashSet<Block>(); _groundedBlocks = new HashSet<Block>(); for (Block block : getNeighbourBlocks(_root)) { if (!_finalSet.contains(block)) { findConnectedLogs(block); } } _leaves = new HashSet<Block>(); for (Block block : _finalSet) { findConnectedLeaves(block, 0); } _finalSet.addAll(_leaves); } catch (BlockLimitException e) { // Block limit reached return nothing. _finalSet = new HashSet<Block>(); } _leaves = null; _groundedBlocks = null; } return _finalSet; } private void findConnectedLogs(Block start) throws BlockLimitException { HashSet<Block> result = new HashSet<Block>(); HashSet<Block> blocks = new HashSet<Block>(); blocks.add(start); while (blocks.size() > 0) { HashSet<Block> lastBlocks = blocks; blocks = new HashSet<Block>(); for (Block block : lastBlocks) { if (!block.equals(_root)) { if (isLog(block.getType())) { if (result.add(block)) { blocks.addAll(getNeighbourBlocks(block)); checkBlockLimit(result.size()); } } else if (findGround(block)) { // Found ground, discard all and mark as grounded. _groundedBlocks.addAll(result); return; } } } } _finalSet.addAll(result); } private List<Block> getNeighbourBlocks(Block block) { ArrayList<Block> blocks = new ArrayList<Block>(); for (int dx = -1; dx <= 1; ++dx) { for (int dy = -1; dy <= 1; ++dy) { for (int dz = -1; dz <= 1; ++dz) { if (dx != 0 || dy != 0 || dz != 0) { blocks.add(block.getRelative(dx, dy, dz)); } } } } return blocks; } private boolean findGround(Block start) { if (_groundedBlocks.contains(start)) { return true; } if (!isGround(start.getType())) { return false; } HashSet<Block> result = new HashSet<Block>(); HashSet<Block> blocks = new HashSet<Block>(); blocks.add(start); while (blocks.size() > 0) { HashSet<Block> lastBlocks = blocks; blocks = new HashSet<Block>(); for (Block block : lastBlocks) { if (isGround(block.getType()) && result.add(block)) { if (result.size() >= 5) { // Found 5 connected ground blocks. // Mark them as ground and return true. _groundedBlocks.addAll(result); return true; } blocks.add(block.getRelative(0, -1, 0)); blocks.add(block.getRelative(0, 1, 0)); blocks.add(block.getRelative(-1, 0, 0)); blocks.add(block.getRelative(1, 0, 0)); blocks.add(block.getRelative(0, 0, -1)); blocks.add(block.getRelative(0, 0, 1)); } } } return false; } private void findConnectedLeaves(Block block, int depth) throws BlockLimitException { if (depth == 0 || isLeaves(block.getType())) { if (depth != 0) { _leaves.add(block); checkBlockLimit(_leaves.size()); } if (depth < 7) { findConnectedLeaves(block.getRelative(0, -1, 0), depth + 1); findConnectedLeaves(block.getRelative(0, 1, 0), depth + 1); findConnectedLeaves(block.getRelative(-1, 0, 0), depth + 1); findConnectedLeaves(block.getRelative(1, 0, 0), depth + 1); findConnectedLeaves(block.getRelative(0, 0, -1), depth + 1); findConnectedLeaves(block.getRelative(0, 0, 1), depth + 1); } } else if (isLog(block.getType())) { // Found log is it a small disconnected log patch? if (!_finalSet.contains(block) && !_groundedBlocks.contains(block) && !_leaves.contains(block)) { HashSet<Block> patch = findSmallLogPatch(block); _leaves.addAll(patch); checkBlockLimit(_leaves.size()); for (Block b : patch) { findConnectedLeaves(b, 2); } } } } private HashSet<Block> findSmallLogPatch(Block start) { HashSet<Block> result = new HashSet<Block>(); HashSet<Block> blocks = new HashSet<Block>(); blocks.add(start); while (blocks.size() > 0) { HashSet<Block> lastBlocks = blocks; blocks = new HashSet<Block>(); for (Block block : lastBlocks) { if (!block.equals(_root)) { if (isLog(block.getType())) { if (result.add(block)) { if (result.size() > 3) { // Found more than 3 connected log blocks. // This is not a small patch. // Here _groundedBlocks acts as a blacklist for patch blocks. _groundedBlocks.addAll(result); return new HashSet<Block>(); } blocks.add(block.getRelative(0, -1, 0)); blocks.add(block.getRelative(0, 1, 0)); blocks.add(block.getRelative(-1, 0, 0)); blocks.add(block.getRelative(1, 0, 0)); blocks.add(block.getRelative(0, 0, -1)); blocks.add(block.getRelative(0, 0, 1)); } } else if (findGround(block)) { // Found ground, this is not a disconnected patch. return new HashSet<Block>(); } } } } return result; } } }