UltimateTimber/src/main/java/com/songoda/ultimatetimber/manager/TreeDetectionManager.java

244 lines
11 KiB
Java
Raw Normal View History

2019-03-28 05:22:13 +01:00
package com.songoda.ultimatetimber.manager;
import com.songoda.ultimatetimber.UltimateTimber;
2019-03-28 21:34:17 +01:00
import com.songoda.ultimatetimber.adapter.IBlockData;
2019-03-28 18:38:37 +01:00
import com.songoda.ultimatetimber.adapter.VersionAdapter;
import com.songoda.ultimatetimber.tree.*;
import org.bukkit.block.Block;
import org.bukkit.block.BlockFace;
import org.bukkit.util.Vector;
import java.util.Comparator;
import java.util.HashSet;
import java.util.Set;
2019-03-28 05:22:13 +01:00
public class TreeDetectionManager extends Manager {
2019-03-28 18:38:37 +01:00
private final Set<Vector> VALID_TRUNK_OFFSETS, VALID_BRANCH_OFFSETS, VALID_LEAF_OFFSETS;
private TreeDefinitionManager treeDefinitionManager;
private int maxBranchBlocksAllowed;
private int numLeavesRequiredForTree;
private boolean allowMixedTreeTypes;
private boolean onlyBreakLogsUpwards;
private boolean destroyBaseLog;
private boolean entireTreeBase;
2019-03-28 05:22:13 +01:00
public TreeDetectionManager(UltimateTimber ultimateTimber) {
super(ultimateTimber);
2019-03-28 18:38:37 +01:00
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));
}
2019-03-28 05:22:13 +01:00
}
@Override
public void reload() {
2019-03-28 18:38:37 +01:00
this.treeDefinitionManager = this.ultimateTimber.getTreeDefinitionManager();
this.maxBranchBlocksAllowed = ConfigurationManager.Setting.MAX_LOGS_PER_CHOP.getInt();
this.numLeavesRequiredForTree = ConfigurationManager.Setting.LEAVES_REQUIRED_FOR_TREE.getInt();
this.allowMixedTreeTypes = ConfigurationManager.Setting.MIX_ALL_TREE_TYPES.getBoolean();
this.onlyBreakLogsUpwards = ConfigurationManager.Setting.ONLY_DETECT_LOGS_UPWARDS.getBoolean();
this.destroyBaseLog = ConfigurationManager.Setting.DESTROY_INITIATED_BLOCK.getBoolean();
this.entireTreeBase = ConfigurationManager.Setting.BREAK_ENTIRE_TREE_BASE.getBoolean();
2019-03-28 05:22:13 +01:00
}
@Override
public void disable() {
}
2019-03-28 18:38:37 +01:00
/**
* Detects a tree given an initial starting block
*
* @param initialBlock The starting Block of the detection
* @return A DetectedTree if one was found, otherwise null
*/
public DetectedTree detectTree(Block initialBlock) {
TreeBlock initialTreeBlock = new TreeBlock(initialBlock, TreeBlockType.LOG);
TreeBlockSet<Block> detectedTreeBlocks = new TreeBlockSet<>(initialTreeBlock);
2019-03-28 21:34:17 +01:00
Set<TreeDefinition> possibleTreeDefinitions = treeDefinitionManager.getTreeDefinitionsForLog(initialBlock);
2019-03-28 18:38:37 +01:00
// Detect tree trunk
Set<Block> trunkBlocks = new HashSet<>();
trunkBlocks.add(initialBlock);
Block targetBlock = initialBlock;
while (this.isValidLogType(possibleTreeDefinitions, (targetBlock = targetBlock.getRelative(BlockFace.UP)))) {
TreeBlock treeBlock = new TreeBlock(targetBlock, TreeBlockType.LOG);
detectedTreeBlocks.add(treeBlock);
trunkBlocks.add(initialBlock);
2019-03-28 21:34:17 +01:00
possibleTreeDefinitions.retainAll(this.treeDefinitionManager.narrowTreeDefinition(possibleTreeDefinitions, targetBlock, TreeBlockType.LOG));
2019-03-28 18:38:37 +01:00
}
// Tree must be at least 2 blocks tall
if (detectedTreeBlocks.size() < 2)
return null;
// Detect branches off the main trunk
for (Block trunkBlock : trunkBlocks)
this.recursiveBranchSearch(possibleTreeDefinitions, detectedTreeBlocks, trunkBlock, initialBlock.getY());
// Detect leaves off the trunk/branches
Set<ITreeBlock<Block>> branchBlocks = new HashSet<>(detectedTreeBlocks.getLogBlocks());
for (ITreeBlock<Block> branchBlock : branchBlocks)
this.recursiveLeafSearch(possibleTreeDefinitions, detectedTreeBlocks, branchBlock.getBlock(), 1);
2019-03-28 21:34:17 +01:00
TreeDefinition actualTreeDefinition = possibleTreeDefinitions.iterator().next();
2019-03-28 18:38:37 +01:00
// Trees need at least a certain number of leaves
if (detectedTreeBlocks.getLeafBlocks().size() < this.numLeavesRequiredForTree)
return null;
2019-03-28 23:19:52 +01:00
// Check that the tree isn't on the ground if enabled
2019-03-28 21:34:17 +01:00
if (this.entireTreeBase) {
2019-03-28 23:19:52 +01:00
Set<Block> groundBlocks = new HashSet<>();
for (ITreeBlock<Block> treeBlock : detectedTreeBlocks.getLogBlocks())
if (treeBlock.getLocation().getBlockY() == initialBlock.getLocation().getBlockY())
groundBlocks.add(treeBlock.getBlock());
for (Block block : groundBlocks) {
Block blockBelow = block.getRelative(BlockFace.DOWN);
boolean blockBelowIsLog = this.isValidLogType(possibleTreeDefinitions, blockBelow);
boolean blockBelowIsSoil = false;
for (IBlockData blockData : actualTreeDefinition.getPlantableSoilBlockData()) {
if (blockData.isSimilar(blockBelow)) {
blockBelowIsSoil = true;
break;
}
}
if (!blockBelowIsLog && blockBelowIsSoil)
return null;
}
2019-03-28 21:34:17 +01:00
}
2019-03-28 18:38:37 +01:00
// Delete the starting block if applicable
if (this.destroyBaseLog)
detectedTreeBlocks.remove(initialTreeBlock);
// Use the first tree definition in the set
2019-03-28 21:34:17 +01:00
return new DetectedTree(actualTreeDefinition, detectedTreeBlocks);
2019-03-28 18:38:37 +01:00
}
/**
* Recursively searches for branches off a given block
*
* @param treeDefinitions The possible tree definitions
* @param treeBlocks The detected tree blocks
* @param block The next block to check for a branch
* @param startingBlockY The Y coordinate of the initial block
*/
private void recursiveBranchSearch(Set<TreeDefinition> treeDefinitions, TreeBlockSet<Block> treeBlocks, Block block, int startingBlockY) {
if (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());
TreeBlock treeBlock = new TreeBlock(targetBlock, TreeBlockType.LOG);
if (this.isValidLogType(treeDefinitions, targetBlock) && !treeBlocks.contains(treeBlock)) {
treeBlocks.add(treeBlock);
2019-03-28 21:34:17 +01:00
treeDefinitions.retainAll(this.treeDefinitionManager.narrowTreeDefinition(treeDefinitions, block, TreeBlockType.LOG));
2019-03-28 18:38:37 +01:00
if (!this.onlyBreakLogsUpwards || targetBlock.getLocation().getBlockY() > startingBlockY)
this.recursiveBranchSearch(treeDefinitions, treeBlocks, targetBlock, startingBlockY);
}
}
}
/**
* Recursively searches for leaves that are next to this tree
*
* @param treeDefinitions The possible tree definitions
* @param treeBlocks The detected tree blocks
* @param block The next block to check for a leaf
* @param distanceFromLog The distance this leaf is from a log
*/
private void recursiveLeafSearch(Set<TreeDefinition> treeDefinitions, TreeBlockSet<Block> treeBlocks, Block block, int distanceFromLog) {
int maxDistanceFromLog = treeDefinitions.stream().max(Comparator.comparingInt(TreeDefinition::getMaxLeafDistanceFromLog)).get().getMaxLeafDistanceFromLog();
boolean detectLeavesDiagonally = treeDefinitions.stream().anyMatch(TreeDefinition::shouldDetectLeavesDiagonally);
if (distanceFromLog > maxDistanceFromLog)
return;
for (Vector offset : !detectLeavesDiagonally ? VALID_LEAF_OFFSETS : VALID_TRUNK_OFFSETS) {
Block targetBlock = block.getRelative(offset.getBlockX(), offset.getBlockY(), offset.getBlockZ());
TreeBlock treeBlock = new TreeBlock(targetBlock, TreeBlockType.LEAF);
if (this.isValidLeafType(treeDefinitions, targetBlock)) {
if (!treeBlocks.contains(treeBlock) && !this.doesLeafBorderInvalidLog(treeDefinitions, treeBlocks, targetBlock)) {
treeBlocks.add(treeBlock);
2019-03-28 21:34:17 +01:00
treeDefinitions.retainAll(this.treeDefinitionManager.narrowTreeDefinition(treeDefinitions, block, TreeBlockType.LEAF));
2019-03-28 18:38:37 +01:00
}
this.recursiveLeafSearch(treeDefinitions, treeBlocks, targetBlock, distanceFromLog + 1);
}
}
}
/**
* Checks if a leaf is bordering a log that isn't part of this tree
*
* @param treeDefinitions The possible tree definitions
* @param treeBlocks The detected tree blocks
* @param block The block to check
* @return True if the leaf borders an invalid log, otherwise false
*/
private boolean doesLeafBorderInvalidLog(Set<TreeDefinition> treeDefinitions, TreeBlockSet<Block> treeBlocks, Block block) {
for (Vector offset : VALID_TRUNK_OFFSETS) {
Block targetBlock = block.getRelative(offset.getBlockX(), offset.getBlockY(), offset.getBlockZ());
if (this.isValidLogType(treeDefinitions, targetBlock) && !treeBlocks.contains(new TreeBlock(targetBlock, TreeBlockType.LOG)))
return true;
}
return false;
}
/**
* Checks if a given block is valid for the given TreeDefinitions
*
* @param treeDefinitions The Set of TreeDefinitions to compare against
* @param block The Block to check
* @return True if the block is a valid log type, otherwise false
*/
private boolean isValidLogType(Set<TreeDefinition> treeDefinitions, Block block) {
VersionAdapter versionAdapter = this.ultimateTimber.getVersionAdapter();
for (TreeDefinition treeDefinition : treeDefinitions)
2019-03-28 21:34:17 +01:00
for (IBlockData logBlockData : treeDefinition.getLogBlockData())
if (logBlockData.isSimilar(block))
2019-03-28 18:38:37 +01:00
return true;
return false;
}
/**
* Checks if a given block is valid for the given TreeDefinitions
*
* @param treeDefinitions The Set of TreeDefinitions to compare against
* @param block The Block to check
* @return True if the block is a valid log type, otherwise false
*/
private boolean isValidLeafType(Set<TreeDefinition> treeDefinitions, Block block) {
VersionAdapter versionAdapter = this.ultimateTimber.getVersionAdapter();
for (TreeDefinition treeDefinition : treeDefinitions)
2019-03-28 21:34:17 +01:00
for (IBlockData leafBlockData : treeDefinition.getLeafBlockData())
if (leafBlockData.isSimilar(block))
2019-03-28 18:38:37 +01:00
return true;
return false;
}
2019-03-28 05:22:13 +01:00
}