Complete rewrite of tree detection algorithm

This commit is contained in:
Esophose 2019-02-09 04:14:35 -07:00
parent d0cedf4ced
commit bcfdeabb2c
5 changed files with 288 additions and 274 deletions

View File

@ -9,16 +9,15 @@ import java.util.Collections;
public class DefaultConfig { public class DefaultConfig {
/*
This value is just cached so it can easily and safely be accessed during runtime
*/
// public static Configuration configuration;
/* /*
Storing these values in final strings makes it so you can change the keys or refactor their names later on without Storing these values in final strings makes it so you can change the keys or refactor their names later on without
ever having to alter any code directly. ever having to alter any code directly.
Also they are easier to refer to using an IDE. Also they are easier to refer to using an IDE.
*/ */
public static final String MAX_BRANCH_BLOCKS = "Max amount of logs that can be broken with one chop";
public static final String LEAVES_FOR_TREE = "The number of leaves required to detect a valid tree";
public static final String ONLY_BREAK_LOGS_UPWARDS = "Only break logs above the block broken";
public static final String ALLOW_MIXED_TREE_TYPES = "Allow mixed log/leaf to be considered as one tree";
public static final String AXES_ONLY = "Only topple down trees cut down using axes"; public static final String AXES_ONLY = "Only topple down trees cut down using axes";
public static final String TIMEOUT_BREAK = "Five second time out before you can break saplings"; public static final String TIMEOUT_BREAK = "Five second time out before you can break saplings";
public static final String SNEAK_ONLY = "Only topple down trees cut down while sneaking"; public static final String SNEAK_ONLY = "Only topple down trees cut down while sneaking";
@ -32,13 +31,17 @@ public class DefaultConfig {
public static final String CUSTOM_AUDIO = "Use custom sounds for trees falling"; public static final String CUSTOM_AUDIO = "Use custom sounds for trees falling";
public static final String SHOW_ANIMATION = "Show tree fall animation"; public static final String SHOW_ANIMATION = "Show tree fall animation";
public static final String CUSTOM_LOOT_LIST = "Custom loot"; public static final String CUSTOM_LOOT_LIST = "Custom loot";
private static final String CUSTOM_LOOT_ITEM = "Material:GOLDEN_APPLE,Chance:1"; public static final String CUSTOM_LOOT_ITEM = "Material:GOLDEN_APPLE,Chance:1";
public static void initialize() { public static void initialize() {
UltimateTimber plugin = UltimateTimber.getInstance(); UltimateTimber plugin = UltimateTimber.getInstance();
Configuration configuration = plugin.getConfig(); Configuration configuration = plugin.getConfig();
configuration.addDefault(MAX_BRANCH_BLOCKS, 75);
configuration.addDefault(LEAVES_FOR_TREE, 5);
configuration.addDefault(ONLY_BREAK_LOGS_UPWARDS, true);
configuration.addDefault(ALLOW_MIXED_TREE_TYPES, false);
configuration.addDefault(AXES_ONLY, true); configuration.addDefault(AXES_ONLY, true);
configuration.addDefault(TIMEOUT_BREAK, true); configuration.addDefault(TIMEOUT_BREAK, true);
configuration.addDefault(SNEAK_ONLY, false); configuration.addDefault(SNEAK_ONLY, false);
@ -63,7 +66,6 @@ public class DefaultConfig {
plugin.saveConfig(); plugin.saveConfig();
plugin.saveDefaultConfig(); plugin.saveDefaultConfig();
} }
} }

View File

@ -21,7 +21,7 @@ class EventFilter {
*/ */
if (event.isCancelled() if (event.isCancelled()
|| !plugin.isWorldValid(event.getPlayer().getWorld()) || !plugin.isWorldValid(event.getPlayer().getWorld())
|| !TreeChecker.validMaterials.contains(event.getBlock().getType())) return false; || !TreeChecker.getValidWoodMaterials().contains(event.getBlock().getType())) return false;
FileConfiguration fileConfiguration = UltimateTimber.getInstance().getConfig(); FileConfiguration fileConfiguration = UltimateTimber.getInstance().getConfig();

View File

@ -1,286 +1,289 @@
package com.songoda.ultimatetimber.treefall; package com.songoda.ultimatetimber.treefall;
import com.songoda.ultimatetimber.utils.LogToLeafConverter; import java.util.Arrays;
import org.bukkit.Location; import java.util.HashSet;
import java.util.Set;
import org.bukkit.Material; import org.bukkit.Material;
import org.bukkit.block.Block; import org.bukkit.block.Block;
import org.bukkit.block.BlockFace;
import org.bukkit.configuration.file.FileConfiguration;
import org.bukkit.util.Vector; import org.bukkit.util.Vector;
import java.util.*; import com.songoda.ultimatetimber.UltimateTimber;
import com.songoda.ultimatetimber.configurations.DefaultConfig;
import com.songoda.ultimatetimber.utils.LogToLeafConverter;
public class TreeChecker { public class TreeChecker {
/* /*
Used to check if a tree is a tree Used to check if a piece of wood is a part of the tree
*/ */
static List<Material> validMaterials = new ArrayList<>(Arrays.asList( private final static Set<Material> VALID_LOG_MATERIALS = new HashSet<>(Arrays.asList(
Material.ACACIA_LOG, Material.ACACIA_LOG,
Material.ACACIA_WOOD,
Material.STRIPPED_ACACIA_LOG, Material.STRIPPED_ACACIA_LOG,
Material.STRIPPED_ACACIA_WOOD,
Material.BIRCH_LOG, Material.BIRCH_LOG,
Material.BIRCH_WOOD,
Material.STRIPPED_BIRCH_LOG, Material.STRIPPED_BIRCH_LOG,
Material.STRIPPED_BIRCH_WOOD,
Material.DARK_OAK_LOG, Material.DARK_OAK_LOG,
Material.DARK_OAK_WOOD,
Material.STRIPPED_DARK_OAK_LOG, Material.STRIPPED_DARK_OAK_LOG,
Material.STRIPPED_DARK_OAK_WOOD,
Material.JUNGLE_LOG, Material.JUNGLE_LOG,
Material.JUNGLE_WOOD,
Material.STRIPPED_JUNGLE_LOG, Material.STRIPPED_JUNGLE_LOG,
Material.STRIPPED_JUNGLE_WOOD,
Material.OAK_LOG, Material.OAK_LOG,
Material.OAK_WOOD,
Material.STRIPPED_OAK_LOG, Material.STRIPPED_OAK_LOG,
Material.STRIPPED_OAK_WOOD,
Material.SPRUCE_LOG, Material.SPRUCE_LOG,
Material.SPRUCE_WOOD,
Material.STRIPPED_SPRUCE_LOG, Material.STRIPPED_SPRUCE_LOG,
Material.STRIPPED_SPRUCE_WOOD,
Material.MUSHROOM_STEM Material.MUSHROOM_STEM
)); ));
/* /*
Used to limit the blocks that constitute a tree Used to check if a leaf is a part of the tree
*/ */
private static List<Material> validTreeMaterials = new ArrayList<>(Arrays.asList( private final static Set<Material> VALID_LEAF_MATERIALS = new HashSet<>(Arrays.asList(
Material.ACACIA_LEAVES, Material.ACACIA_LEAVES,
Material.BIRCH_LEAVES, Material.BIRCH_LEAVES,
Material.DARK_OAK_LEAVES, Material.DARK_OAK_LEAVES,
Material.JUNGLE_LEAVES, Material.JUNGLE_LEAVES,
Material.OAK_LEAVES, Material.OAK_LEAVES,
Material.SPRUCE_LEAVES, Material.SPRUCE_LEAVES,
Material.COCOA_BEANS,
Material.BROWN_MUSHROOM_BLOCK, Material.BROWN_MUSHROOM_BLOCK,
Material.RED_MUSHROOM_BLOCK Material.RED_MUSHROOM_BLOCK
)); ));
/*
A list of materials found in a forest, allows the plugin to work in dense woods /**
* Gets a Set of all valid wood materials
*
* @return A Set of all valid wood materials
*/ */
private static List<Material> forestMaterials = new ArrayList<>(Arrays.asList( public static Set<Material> getValidWoodMaterials() {
Material.AIR, return VALID_LOG_MATERIALS;
Material.CAVE_AIR,
Material.VOID_AIR,
Material.VINE,
Material.ROSE_BUSH,
Material.ORANGE_TULIP,
Material.PINK_TULIP,
Material.RED_TULIP,
Material.POPPY,
Material.WHITE_TULIP,
Material.OXEYE_DAISY,
Material.AZURE_BLUET,
Material.BLUE_ORCHID,
Material.ALLIUM,
Material.DANDELION,
Material.DANDELION_YELLOW,
Material.LILAC,
Material.PEONY,
Material.TALL_GRASS,
Material.FERN,
Material.LARGE_FERN,
Material.DEAD_BUSH,
Material.BROWN_MUSHROOM,
Material.RED_MUSHROOM,
Material.GRASS,
Material.SPRUCE_SAPLING,
Material.OAK_SAPLING,
Material.JUNGLE_SAPLING,
Material.ACACIA_SAPLING,
Material.BIRCH_SAPLING,
Material.DARK_OAK_SAPLING,
Material.DIRT,
Material.COARSE_DIRT,
Material.GRASS_BLOCK,
Material.SNOW,
Material.SNOW_BLOCK
));
/*
This stores all the blocks returned later on
*/
private HashSet<Block> allBlocks = new HashSet<>();
HashSet<Block> validTreeHandler(Block block) {
HashSet<Block> blocks = parseTree(block);
if (blocks == null)
return null;
boolean containsLeaves = false;
for (Block localBlock : blocks) {
if (TreeChecker.validTreeMaterials.contains(localBlock.getType())) {
containsLeaves = true;
break;
} else if (TreeChecker.validMaterials.contains(localBlock.getType())) {
containsLeaves = true;
break;
}
}
if (!containsLeaves)
return null;
return blocks;
} }
/* private static final Set<Vector> VALID_TRUNK_OFFSETS, VALID_BRANCH_OFFSETS, VALID_LEAF_OFFSETS;
Returns all of the blocks in this tree
*/ private HashSet<Block> treeBlocks;
public HashSet<Block> getAllBlocks(){ private int maxDistanceFromLog;
return allBlocks; private Material logType, leafType;
private int startingBlockY;
private int maxBranchBlocksAllowed;
private int numLeavesRequiredForTree;
private boolean allowMixedTreeTypes;
private boolean onlyBreakLogsUpwards;
private boolean isMushroom = false;
static {
VALID_BRANCH_OFFSETS = new HashSet<>();
VALID_TRUNK_OFFSETS = new HashSet<>();
VALID_LEAF_OFFSETS = new HashSet<>();
// 3x2x3 centered around log, excluding -y axis
for (int x = -1; x <= 1; x++)
for (int y = 0; y <= 1; y++)
for (int z = -1; z <= 1; z++)
VALID_BRANCH_OFFSETS.add(new Vector(x, y, z));
// 3x3x3 centered around log
for (int x = -1; x <= 1; x++)
for (int y = -1; y <= 1; y++)
for (int z = -1; z <= 1; z++)
VALID_TRUNK_OFFSETS.add(new Vector(x, y, z));
// Adjacent blocks to log
for (int i = -1; i <= 1; i += 2) {
VALID_LEAF_OFFSETS.add(new Vector(i, 0, 0));
VALID_LEAF_OFFSETS.add(new Vector(0, i, 0));
VALID_LEAF_OFFSETS.add(new Vector(0, 0, i));
}
} }
/** /**
* This parses a tree; returns a hashset if it is a valid tree, or returns null if it isn't * Parses a block for a potential tree
* *
* @param block block the player originally destroys * @param block The based block of the potential tree
* @return returns all blocks in the tree if it is valid or null if the tree isn't * @return A HashSet of all blocks in the tree, or null if no tree was found
*/ */
private HashSet<Block> parseTree(Block block) { protected HashSet<Block> parseTree(Block block) {
this.treeBlocks = new HashSet<>();
this.treeBlocks.add(block);
/* // Set tree information
Check if material is parsed by this plugin this.logType = block.getType();
*/ this.leafType = LogToLeafConverter.convert(this.logType);
if (!validMaterials.contains(block.getType())) return null; this.startingBlockY = block.getLocation().getBlockY();
this.isMushroom = this.logType.equals(Material.MUSHROOM_STEM);
/* // Load settings for algorithm
offset determines the search radius around the main trunk FileConfiguration config = UltimateTimber.getInstance().getConfig();
maxheight sets the maximum height the plugin will crawl through to find a tree this.allowMixedTreeTypes = config.getBoolean(DefaultConfig.ALLOW_MIXED_TREE_TYPES);
*/ this.maxBranchBlocksAllowed = config.getInt(DefaultConfig.MAX_BRANCH_BLOCKS);
int offset = 7; this.numLeavesRequiredForTree = config.getInt(DefaultConfig.LEAVES_FOR_TREE);
int maxHeight = 31; this.onlyBreakLogsUpwards = config.getBoolean(DefaultConfig.ONLY_BREAK_LOGS_UPWARDS);
/*
Keep track of the location of the original block to see how much we've deviated from it
*/
Location centralBlockLocation = block.getLocation().clone();
/*
Keep a list of all location that are considered to be a part of the trunk. This is necessary as scans are made
around each one as the search crawls up to detect leaves or building blocks.
*/
HashSet<Location> trunkList = new HashSet<>();
trunkList.add(centralBlockLocation);
Material originalMaterial = block.getType();
for (int i = 0; i < maxHeight; i++) {
/*
For some reason, using the iterator to gradually clear hashset elements isn't working as the hashset
claims not to contain said elements. This is a bit of a dirty workarounf dor that issue.
*/
HashSet<Location> cleanLogSet = new HashSet<>();
for (Location location : trunkList)
if (location.getBlock().getType().equals(originalMaterial) ||
location.getBlock().getType().equals(LogToLeafConverter.convert(originalMaterial)) ||
location.clone().add(new Vector(0, -1, 0)).getBlock().getType().equals(originalMaterial))
cleanLogSet.add(location);
if (cleanLogSet.isEmpty()) {
if (i > 2)
return allBlocks;
else
return null;
}
trunkList = cleanLogSet;
/*
Search for adjacent trunks
*/
Iterator<Location> iterator = trunkList.iterator();
HashSet<Location> expandedTrunkSet = new HashSet<>();
while (iterator.hasNext()) {
Location trunkLocation = iterator.next();
allBlocks.add(trunkLocation.getBlock());
int radMin, radMax;
if (i > 5) {
radMin = -2;
radMax = 3;
} else {
radMin = -1;
radMax = 2;
}
for (int x = radMin; x < radMax; x++)
for (int z = radMin; z < radMax; z++) {
Location currentLocation = trunkLocation.clone().add(new Vector(x, 0, z));
if (Math.abs(currentLocation.getX() - trunkLocation.getX()) > offset ||
Math.abs(currentLocation.getZ() - trunkLocation.getZ()) > offset)
continue;
if (currentLocation.getBlock().getType().equals(originalMaterial)) {
expandedTrunkSet.add(currentLocation);
allBlocks.add(currentLocation.getBlock());
}
}
}
trunkList.addAll(expandedTrunkSet);
/*
Check if the tree is valid and add leaves
*/
for (Location location : trunkList) {
int radMin, radMax;
if (i > 5) {
radMin = -4;
radMax = 6;
} else {
radMin = -3;
radMax = 5;
}
for (int x = radMin; x < radMax; x++)
for (int z = radMin; z < radMax; z++) {
Block currentBlock = location.clone().add(x, 0, z).getBlock();
/*
Check if this block is already in the block list
*/
if (allBlocks.contains(currentBlock))
continue;
/*
Add a bit of tolerance for trees that exist on dirt ledges
*/
if ((currentBlock.getType().equals(Material.DIRT) ||
currentBlock.getType().equals(Material.COARSE_DIRT) ||
currentBlock.getType().equals(Material.GRASS_BLOCK)) &&
i > 1) {
return null;
}
/*
Exclude anything that isn't a part of a tree or a forest to avoid destroying houses
*/
if (!validMaterials.contains(currentBlock.getType()) &&
!validTreeMaterials.contains(currentBlock.getType()) &&
!forestMaterials.contains(currentBlock.getType()))
return null;
/*
This adds blocks to later be felled
Only take blocks of the same tree type
*/
if ((LogToLeafConverter.convert(originalMaterial) != null &&
LogToLeafConverter.convert(originalMaterial).equals(currentBlock.getType())) ||
(originalMaterial.equals(Material.MUSHROOM_STEM) &&
(currentBlock.getType().equals(Material.RED_MUSHROOM_BLOCK) ||
currentBlock.getType().equals(Material.BROWN_MUSHROOM_BLOCK)))) {
allBlocks.add(currentBlock);
}
}
location.add(new Vector(0, 1, 0));
}
// Detect tree trunk
Set<Block> trunkBlocks = new HashSet<>();
trunkBlocks.add(block);
Block targetBlock = block;
while (this.isValidLogType((targetBlock = targetBlock.getRelative(BlockFace.UP)).getType())) {
this.treeBlocks.add(targetBlock);
trunkBlocks.add(targetBlock);
} }
return allBlocks; // Tree must be at least 2 blocks tall
if (this.treeBlocks.size() < 2)
return null;
// Detect branches off the main trunk
for (Block trunkBlock : trunkBlocks)
this.recursiveBranchSearch(trunkBlock);
// Make it so trees only break as many leaves as they have to
this.maxDistanceFromLog = this.getMaxLeafDistanceFromLog();
// Detect leaves off the trunk/branches
Set<Block> branchBlocks = new HashSet<Block>(this.treeBlocks);
for (Block branchBlock : branchBlocks)
this.recursiveLeafSearch(branchBlock, 1);
// Trees need at least 5 leaves
if (!this.isMushroom && this.treeBlocks.stream().filter(x -> this.isValidLeafType(x.getType())).count() < this.numLeavesRequiredForTree)
return null;
return this.treeBlocks;
}
/**
* Recursively searches for branches off a given block
*
* @param block The next block to check for a branch
*/
private void recursiveBranchSearch(Block block) {
if (this.treeBlocks.size() > this.maxBranchBlocksAllowed)
return;
for (Vector offset : this.onlyBreakLogsUpwards ? VALID_BRANCH_OFFSETS : VALID_TRUNK_OFFSETS) {
Block targetBlock = block.getRelative(offset.getBlockX(), offset.getBlockY(), offset.getBlockZ());
if (this.isValidLogType(targetBlock.getType()) && !this.treeBlocks.contains(targetBlock)) {
this.treeBlocks.add(targetBlock);
if (!this.onlyBreakLogsUpwards || targetBlock.getLocation().getBlockY() > this.startingBlockY)
this.recursiveBranchSearch(targetBlock);
}
}
}
/**
* Recursively searches for leaves that are next to this tree
*
* @param block The next block to check for a leaf
* @param distanceFromLog The distance this leaf is from a log
*/
private void recursiveLeafSearch(Block block, int distanceFromLog) {
if (distanceFromLog > this.maxDistanceFromLog)
return;
for (Vector offset : !this.isMushroom ? VALID_LEAF_OFFSETS : VALID_TRUNK_OFFSETS) {
Block targetBlock = block.getRelative(offset.getBlockX(), offset.getBlockY(), offset.getBlockZ());
if (this.isValidLeafType(targetBlock.getType()) || (this.isMushroom && this.isMushroomBlock(targetBlock.getType()))) {
if (!this.treeBlocks.contains(targetBlock) && !doesLeafBorderInvalidLog(targetBlock))
this.treeBlocks.add(targetBlock);
this.recursiveLeafSearch(targetBlock, distanceFromLog + 1);
}
}
}
/**
* Checks if a leaf is bordering a log that isn't part of this tree
*
* @param block The block to check
* @return If the leaf borders an invalid log
*/
private boolean doesLeafBorderInvalidLog(Block block) {
for (Vector offset : VALID_TRUNK_OFFSETS) {
Block targetBlock = block.getRelative(offset.getBlockX(), offset.getBlockY(), offset.getBlockZ());
if (this.isValidLogType(targetBlock.getType()) && !this.treeBlocks.contains(targetBlock))
return true;
}
return false;
}
private boolean isValidLogType(Material material) {
if (this.allowMixedTreeTypes)
return VALID_LOG_MATERIALS.contains(material);
return material.equals(this.logType);
}
private boolean isValidLeafType(Material material) {
if (this.allowMixedTreeTypes)
return VALID_LEAF_MATERIALS.contains(material);
return material.equals(this.leafType);
}
/**
* Checks if a block is a mushroom head block
*
* @param block The block to check
* @return If the given block is a mushroom
*/
private boolean isMushroomBlock(Material material) {
return material.equals(Material.BROWN_MUSHROOM_BLOCK) || material.equals(Material.RED_MUSHROOM_BLOCK);
}
/**
* Gets the max distance away from a log based on how many logs there are and the leaf type
*
* @return The max distance away a leaf can be from a log
*/
private int getMaxLeafDistanceFromLog() {
int numLogs = this.treeBlocks.size();
switch (this.leafType) {
case ACACIA_LEAVES:
return 5;
case BIRCH_LEAVES:
return 4;
case DARK_OAK_LEAVES:
return 5;
case JUNGLE_LEAVES:
if (numLogs > 15)
return 5;
return 4;
case OAK_LEAVES:
if (numLogs > 15)
return 6;
if (numLogs > 6)
return 5;
return 4;
case SPRUCE_LEAVES:
if (numLogs > 15)
return 6;
return 5;
case MUSHROOM_STEM:
return 4;
default:
return -1;
}
}
public HashSet<Block> getTreeBlocks() {
return this.treeBlocks;
} }
} }

View File

@ -36,7 +36,7 @@ public class TreeFallListener implements Listener {
if (!UltimateTimber.getInstance().isChopping(event.getPlayer())) return; if (!UltimateTimber.getInstance().isChopping(event.getPlayer())) return;
TreeChecker treeChecker = new TreeChecker(); TreeChecker treeChecker = new TreeChecker();
HashSet<Block> blocks = treeChecker.validTreeHandler(event.getBlock()); HashSet<Block> blocks = treeChecker.parseTree(event.getBlock());
/* /*
Previous list will be null if no valid tree is found Previous list will be null if no valid tree is found

View File

@ -1,36 +1,45 @@
package com.songoda.ultimatetimber.utils; package com.songoda.ultimatetimber.utils;
import java.util.HashMap;
import java.util.Map;
import org.bukkit.Material; import org.bukkit.Material;
public class LogToLeafConverter { public class LogToLeafConverter {
private static Map<Material, Material> logToLeaf;
static {
logToLeaf = new HashMap<>();
logToLeaf.put(Material.ACACIA_LOG, Material.ACACIA_LEAVES);
logToLeaf.put(Material.ACACIA_WOOD, Material.ACACIA_LEAVES);
logToLeaf.put(Material.STRIPPED_ACACIA_LOG, Material.ACACIA_LEAVES);
logToLeaf.put(Material.STRIPPED_ACACIA_WOOD, Material.ACACIA_LEAVES);
logToLeaf.put(Material.BIRCH_LOG, Material.BIRCH_LEAVES);
logToLeaf.put(Material.BIRCH_WOOD, Material.BIRCH_LEAVES);
logToLeaf.put(Material.STRIPPED_BIRCH_LOG, Material.BIRCH_LEAVES);
logToLeaf.put(Material.STRIPPED_BIRCH_WOOD, Material.BIRCH_LEAVES);
logToLeaf.put(Material.DARK_OAK_LOG, Material.DARK_OAK_LEAVES);
logToLeaf.put(Material.DARK_OAK_WOOD, Material.DARK_OAK_LEAVES);
logToLeaf.put(Material.STRIPPED_DARK_OAK_LOG, Material.DARK_OAK_LEAVES);
logToLeaf.put(Material.STRIPPED_DARK_OAK_WOOD, Material.DARK_OAK_LEAVES);
logToLeaf.put(Material.JUNGLE_LOG, Material.JUNGLE_LEAVES);
logToLeaf.put(Material.JUNGLE_WOOD, Material.JUNGLE_LEAVES);
logToLeaf.put(Material.STRIPPED_JUNGLE_LOG, Material.JUNGLE_LEAVES);
logToLeaf.put(Material.STRIPPED_JUNGLE_WOOD, Material.JUNGLE_LEAVES);
logToLeaf.put(Material.OAK_LOG, Material.OAK_LEAVES);
logToLeaf.put(Material.OAK_WOOD, Material.OAK_LEAVES);
logToLeaf.put(Material.STRIPPED_OAK_LOG, Material.OAK_LEAVES);
logToLeaf.put(Material.STRIPPED_OAK_WOOD, Material.OAK_LEAVES);
logToLeaf.put(Material.SPRUCE_LOG, Material.SPRUCE_LEAVES);
logToLeaf.put(Material.SPRUCE_WOOD, Material.SPRUCE_LEAVES);
logToLeaf.put(Material.STRIPPED_SPRUCE_LOG, Material.SPRUCE_LEAVES);
logToLeaf.put(Material.STRIPPED_SPRUCE_WOOD, Material.SPRUCE_LEAVES);
logToLeaf.put(Material.MUSHROOM_STEM, Material.MUSHROOM_STEM);
}
public static Material convert(Material material) { public static Material convert(Material material) {
return logToLeaf.get(material);
switch (material) {
case ACACIA_LOG:
case STRIPPED_ACACIA_LOG:
return Material.ACACIA_LEAVES;
case BIRCH_LOG:
case STRIPPED_BIRCH_LOG:
return Material.BIRCH_LEAVES;
case DARK_OAK_LOG:
case STRIPPED_DARK_OAK_LOG:
return Material.DARK_OAK_LEAVES;
case JUNGLE_LOG:
case STRIPPED_JUNGLE_LOG:
return Material.JUNGLE_LEAVES;
case OAK_LOG:
case STRIPPED_OAK_LOG:
return Material.OAK_LEAVES;
case SPRUCE_LOG:
case STRIPPED_SPRUCE_LOG:
return Material.SPRUCE_LEAVES;
default:
return null;
}
} }
} }