Tree detector, file formatting, this., new settings, other changes

This commit is contained in:
Esophose 2019-03-29 16:52:17 -06:00
parent 94ffdcc5f5
commit 8026babcdf
32 changed files with 400 additions and 394 deletions

View File

@ -31,6 +31,7 @@ public class UltimateTimber extends JavaPlugin {
private CommandManager commandManager;
private ConfigurationManager configurationManager;
private HookManager hookManager;
private SaplingManager saplingManager;
private TreeAnimationManager treeAnimationManager;
private TreeDefinitionManager treeDefinitionManager;
private TreeDetectionManager treeDetectionManager;
@ -44,15 +45,16 @@ public class UltimateTimber extends JavaPlugin {
public void onEnable() {
INSTANCE = this;
console.sendMessage(Methods.formatText("&a============================="));
console.sendMessage(Methods.formatText("&7" + this.getDescription().getName() + " " + this.getDescription().getVersion() + " by &5Songoda <3&7!"));
console.sendMessage(Methods.formatText("&7Action: &aEnabling&7..."));
this.console.sendMessage(Methods.formatText("&a============================="));
this.console.sendMessage(Methods.formatText("&7" + this.getDescription().getName() + " " + this.getDescription().getVersion() + " by &5Songoda <3&7!"));
this.console.sendMessage(Methods.formatText("&7Action: &aEnabling&7..."));
this.managers = new HashSet<>();
this.choppingManager = this.registerManager(ChoppingManager.class);
this.commandManager = this.registerManager(CommandManager.class);
this.configurationManager = new ConfigurationManager(this);
this.hookManager = this.registerManager(HookManager.class);
this.saplingManager = this.registerManager(SaplingManager.class);
this.treeAnimationManager = this.registerManager(TreeAnimationManager.class);
this.treeDefinitionManager = this.registerManager(TreeDefinitionManager.class);
this.treeDetectionManager = this.registerManager(TreeDetectionManager.class);
@ -62,19 +64,19 @@ public class UltimateTimber extends JavaPlugin {
this.reload();
new Metrics(this);
console.sendMessage(Methods.formatText("&a============================="));
this.console.sendMessage(Methods.formatText("&a============================="));
}
@Override
public void onDisable() {
console.sendMessage(Methods.formatText("&a============================="));
console.sendMessage(Methods.formatText("&7" + this.getDescription().getName() + " " + this.getDescription().getVersion() + " by &5Songoda <3&7!"));
console.sendMessage(Methods.formatText("&7Action: &cDisabling&7..."));
this.console.sendMessage(Methods.formatText("&a============================="));
this.console.sendMessage(Methods.formatText("&7" + this.getDescription().getName() + " " + this.getDescription().getVersion() + " by &5Songoda <3&7!"));
this.console.sendMessage(Methods.formatText("&7Action: &cDisabling&7..."));
this.disable();
console.sendMessage(Methods.formatText("&a============================="));
this.console.sendMessage(Methods.formatText("&a============================="));
}
/**
@ -164,7 +166,7 @@ public class UltimateTimber extends JavaPlugin {
* @return The ConfigurationManager instance
*/
public ConfigurationManager getConfigurationManager() {
return configurationManager;
return this.configurationManager;
}
/**
@ -173,7 +175,16 @@ public class UltimateTimber extends JavaPlugin {
* @return The HookManager instance
*/
public HookManager getHookManager() {
return hookManager;
return this.hookManager;
}
/**
* Gets the sapling manager
*
* @return The SaplingManager instance
*/
public SaplingManager getSaplingManager() {
return this.saplingManager;
}
/**
@ -182,7 +193,7 @@ public class UltimateTimber extends JavaPlugin {
* @return The TreeAnimationManager instance
*/
public TreeAnimationManager getTreeAnimationManager() {
return treeAnimationManager;
return this.treeAnimationManager;
}
/**
@ -191,7 +202,7 @@ public class UltimateTimber extends JavaPlugin {
* @return The TreeDefinitionManager instance
*/
public TreeDefinitionManager getTreeDefinitionManager() {
return treeDefinitionManager;
return this.treeDefinitionManager;
}
/**
@ -200,7 +211,7 @@ public class UltimateTimber extends JavaPlugin {
* @return The TreeDetectionManager instance
*/
public TreeDetectionManager getTreeDetectionManager() {
return treeDetectionManager;
return this.treeDetectionManager;
}
/**
@ -209,7 +220,7 @@ public class UltimateTimber extends JavaPlugin {
* @return The TreeFallManager instance
*/
public TreeFallManager getTreeFallManager() {
return treeFallManager;
return this.treeFallManager;
}
}

View File

@ -33,4 +33,11 @@ public interface IBlockData {
*/
boolean isSimilar(Block block);
/**
* Sets a given Block to use this IBlockData
*
* @param block The Block to set
*/
void setBlock(Block block);
}

View File

@ -5,6 +5,7 @@ import com.songoda.ultimatetimber.tree.TreeBlock;
import com.songoda.ultimatetimber.tree.TreeBlockSet;
import org.bukkit.block.Block;
import org.bukkit.entity.Player;
import org.bukkit.event.block.BlockBreakEvent;
import org.bukkit.inventory.ItemStack;
import java.util.Collection;
@ -45,10 +46,19 @@ public interface VersionAdapter {
/**
* Applies damage to a tool
*
* @param treeBlocks The Set of tree blocks that are being broken
* @param tool The tool to apply damage to
* @param damage The amount of damage to apply
*/
void applyToolDurability(TreeBlockSet<Block> treeBlocks, ItemStack tool);
void applyToolDurability(ItemStack tool, int damage);
/**
* Checks if a given tool has enough durability remaining
*
* @param tool The tool to check
* @param requiredAmount The amount of durability required
* @return True if enough durability is remaining to not break the tool, otherwise false
*/
boolean hasEnoughDurability(ItemStack tool, int requiredAmount);
/**
* Gets the item in the player's main hand

View File

@ -35,13 +35,18 @@ public class CurrentAdapter implements VersionAdapter {
}
@Override
public void applyToolDurability(TreeBlockSet<Block> treeBlocks, ItemStack tool) {
public void applyToolDurability(ItemStack tool, int damage) {
}
@Override
public boolean hasEnoughDurability(ItemStack tool, int requiredAmount) {
return false;
}
@Override
public ItemStack getItemInHand(Player player) {
return null;
return player.getInventory().getItemInMainHand();
}
@Override

View File

@ -32,4 +32,9 @@ public class CurrentBlockData implements IBlockData {
return this.material.equals(block.getType());
}
@Override
public void setBlock(Block block) {
block.setType(this.material);
}
}

View File

@ -12,6 +12,7 @@ import org.bukkit.inventory.ItemStack;
import java.util.Collection;
@SuppressWarnings("deprecation")
public class LegacyAdapter implements VersionAdapter {
@Override
@ -35,13 +36,18 @@ public class LegacyAdapter implements VersionAdapter {
}
@Override
public void applyToolDurability(TreeBlockSet<Block> treeBlocks, ItemStack tool) {
public void applyToolDurability(ItemStack tool, int damage) {
}
@Override
public boolean hasEnoughDurability(ItemStack tool, int requiredAmount) {
return false;
}
@Override
public ItemStack getItemInHand(Player player) {
return null;
return player.getItemInHand();
}
@Override

View File

@ -37,4 +37,11 @@ public class LegacyBlockData implements IBlockData {
return this.material.equals(blockMaterial) && this.data == blockData;
}
@Override
public void setBlock(Block block) {
block.setType(this.material);
// TODO: Break into maven modules so this can use a 1.12.2 jar for compiling instead
// block.setData(this.data);
}
}

View File

@ -1,7 +1,6 @@
package com.songoda.ultimatetimber.events;
import com.songoda.ultimatetimber.tree.TreeBlockSet;
import org.bukkit.block.Block;
import com.songoda.ultimatetimber.tree.DetectedTree;
import org.bukkit.entity.Player;
import org.bukkit.event.player.PlayerEvent;
@ -10,11 +9,11 @@ import org.bukkit.event.player.PlayerEvent;
*/
public abstract class TreeEvent extends PlayerEvent {
protected final TreeBlockSet<Block> treeBlocks;
protected final DetectedTree detectedTree;
public TreeEvent(Player player, TreeBlockSet<Block> treeBlocks) {
public TreeEvent(Player player, DetectedTree detectedTree) {
super(player);
this.treeBlocks = treeBlocks;
this.detectedTree = detectedTree;
}
/**
@ -22,8 +21,8 @@ public abstract class TreeEvent extends PlayerEvent {
*
* @return The blocks that are part of the tree
*/
public TreeBlockSet<Block> getTreeBlocks() {
return this.treeBlocks;
public DetectedTree getDetectedTree() {
return this.detectedTree;
}
}

View File

@ -1,7 +1,6 @@
package com.songoda.ultimatetimber.events;
import com.songoda.ultimatetimber.tree.TreeBlockSet;
import org.bukkit.block.Block;
import com.songoda.ultimatetimber.tree.DetectedTree;
import org.bukkit.entity.Player;
import org.bukkit.event.Cancellable;
import org.bukkit.event.HandlerList;
@ -13,8 +12,8 @@ public class TreeFallEvent extends TreeEvent implements Cancellable {
private boolean cancelled = false;
public TreeFallEvent(Player player, TreeBlockSet<Block> treeBlocks) {
super(player, treeBlocks);
public TreeFallEvent(Player player, DetectedTree detectedTree) {
super(player, detectedTree);
}
private static final HandlerList handlers = new HandlerList();

View File

@ -1,7 +1,6 @@
package com.songoda.ultimatetimber.events;
import com.songoda.ultimatetimber.tree.TreeBlockSet;
import org.bukkit.block.Block;
import com.songoda.ultimatetimber.tree.DetectedTree;
import org.bukkit.entity.Player;
import org.bukkit.event.HandlerList;
@ -10,8 +9,8 @@ import org.bukkit.event.HandlerList;
*/
public class TreeFellEvent extends TreeEvent {
public TreeFellEvent(Player player, TreeBlockSet<Block> treeBlocks) {
super(player, treeBlocks);
public TreeFellEvent(Player player, DetectedTree detectedTree) {
super(player, detectedTree);
}
private static final HandlerList handlers = new HandlerList();

View File

@ -22,21 +22,21 @@ public class McMMOHook implements TimberHook {
for (Object enumValue : primarySkillTypeClass.getEnumConstants()) {
Enum<?> primarySkillTypeEnum = (Enum<?>) enumValue;
if (primarySkillTypeEnum.name().equals("WOODCUTTING")) {
woodcuttingEnum = primarySkillTypeEnum;
this.woodcuttingEnum = primarySkillTypeEnum;
break;
}
}
getXpMethod = ExperienceConfig.class.getMethod("getXp", woodcuttingEnum.getClass(), Material.class);
this.getXpMethod = ExperienceConfig.class.getMethod("getXp", this.woodcuttingEnum.getClass(), Material.class);
} catch (Exception ex) {
Class<?> skillTypeClass = Class.forName("com.gmail.nossr50.datatypes.skills.SkillType");
for (Object enumValue : skillTypeClass.getEnumConstants()) {
Enum<?> skillTypeEnum = (Enum<?>) enumValue;
if (skillTypeEnum.name().equals("WOODCUTTING")) {
woodcuttingEnum = skillTypeEnum;
this.woodcuttingEnum = skillTypeEnum;
break;
}
}
getXpMethod = ExperienceConfig.class.getMethod("getXp", woodcuttingEnum.getClass(), Material.class);
this.getXpMethod = ExperienceConfig.class.getMethod("getXp", this.woodcuttingEnum.getClass(), Material.class);
}
}
@ -50,7 +50,7 @@ public class McMMOHook implements TimberHook {
Block block = treeBlock.getBlock();
Material material = block.getType();
if (!material.name().endsWith("LOG")) continue;
xp += (int) getXpMethod.invoke(ExperienceConfig.getInstance(), woodcuttingEnum, material);
xp += (int) this.getXpMethod.invoke(ExperienceConfig.getInstance(), this.woodcuttingEnum, material);
}
ExperienceAPI.addXP(player, "woodcutting", xp, "pve");

View File

@ -35,7 +35,7 @@ public class CommandManager extends Manager implements CommandExecutor, TabCompl
public boolean onCommand(CommandSender commandSender, Command command, String s, String[] args) {
if (args.length > 0) {
if (args[0].equalsIgnoreCase("reload")) {
if (commandSender instanceof Player && !permCheck((Player) commandSender, "ultimatetimber.reload"))
if (commandSender instanceof Player && !this.permCheck((Player) commandSender, "ultimatetimber.reload"))
return true;
UltimateTimber.getInstance().reload();
@ -47,7 +47,7 @@ public class CommandManager extends Manager implements CommandExecutor, TabCompl
return true;
}
if (!permCheck((Player) commandSender, "ultimatetimber.toggle"))
if (!this.permCheck((Player) commandSender, "ultimatetimber.toggle"))
return true;
if (UltimateTimber.getInstance().getChoppingManager().togglePlayer((Player)commandSender)) {
@ -81,7 +81,7 @@ public class CommandManager extends Manager implements CommandExecutor, TabCompl
public List<String> onTabComplete(CommandSender commandSender, Command command, String s, String[] args) {
List<String> completions = new ArrayList<>();
if (args.length == 0 || args.length > 1)
if (args.length < 1)
return completions;
Set<String> possibleCompletions = new HashSet<>();

View File

@ -10,10 +10,12 @@ import java.util.List;
public class ConfigurationManager extends Manager {
public enum Setting {
SERVER_TYPE,
DISABLED_WORLDS,
MAX_LOGS_PER_CHOP,
LEAVES_REQUIRED_FOR_TREE,
REALISTIC_TOOL_DAMAGE,
PROTECT_TOOL,
BREAK_ENTIRE_TREE_BASE,
DESTROY_INITIATED_BLOCK,
ONLY_DETECT_LOGS_UPWARDS,
@ -29,6 +31,8 @@ public class ConfigurationManager extends Manager {
FALLING_BLOCK_DAMAGE,
ADD_ITEMS_TO_INVENTORY,
USE_CUSTOM_SOUNDS,
USE_CUSTOM_PARTICLES,
BONUS_LOOT_MULTIPLIER,
SCATTER_TREE_BLOCKS_ON_GROUND,
MIX_ALL_TREE_TYPES;
@ -41,7 +45,7 @@ public class ConfigurationManager extends Manager {
*/
public boolean getBoolean() {
this.loadValue();
return (boolean)value;
return (boolean)this.value;
}
/**
@ -51,7 +55,27 @@ public class ConfigurationManager extends Manager {
*/
public int getInt() {
this.loadValue();
return (int)value;
return (int)this.value;
}
/**
* Gets the setting as a double
*
* @return The setting a double
*/
public double getDouble() {
this.loadValue();
return (double)this.value;
}
/**
* Gets the setting as a String
*
* @return The setting a String
*/
public String getString() {
this.loadValue();
return (String)this.value;
}
/**
@ -62,7 +86,7 @@ public class ConfigurationManager extends Manager {
@SuppressWarnings("unchecked")
public List<String> getStringList() {
this.loadValue();
return (List<String>)value;
return (List<String>)this.value;
}
/**
@ -77,7 +101,7 @@ public class ConfigurationManager extends Manager {
*/
private void loadValue() {
if (this.value == null)
value = UltimateTimber.getInstance().getConfigurationManager().getConfig().get(this.getNameAsKey());
this.value = UltimateTimber.getInstance().getConfigurationManager().getConfig().get(this.getNameAsKey());
}
/**
@ -98,21 +122,21 @@ public class ConfigurationManager extends Manager {
@Override
public void reload() {
File configFile = new File(ultimateTimber.getDataFolder() + "/config.yml");
File configFile = new File(this.ultimateTimber.getDataFolder() + "/config.yml");
// If an old config still exists, rename it so it doesn't interfere
if (configFile.exists() && ultimateTimber.getConfig().get("server-type") == null) {
File renameConfigTo = new File(ultimateTimber.getDataFolder() + "/config-old.yml");
if (configFile.exists() && this.ultimateTimber.getConfig().get("server-type") == null) {
File renameConfigTo = new File(this.ultimateTimber.getDataFolder() + "/config-old.yml");
configFile.renameTo(renameConfigTo);
configFile = new File(ultimateTimber.getDataFolder() + "/config.yml");
configFile = new File(this.ultimateTimber.getDataFolder() + "/config.yml");
}
// Create the new config if it doesn't exist
if (!configFile.exists()) {
boolean isCurrentConfig = ultimateTimber.getVersionAdapter().getVersionAdapterType() == VersionAdapterType.CURRENT;
boolean isCurrentConfig = this.ultimateTimber.getVersionAdapter().getVersionAdapterType() == VersionAdapterType.CURRENT;
String newConfigName = "config-" + (isCurrentConfig ? "current" : "legacy") + ".yml";
File newConfigFile = new File(ultimateTimber.getDataFolder() + "/" + newConfigName);
ultimateTimber.saveResource(newConfigName, false);
File newConfigFile = new File(this.ultimateTimber.getDataFolder() + "/" + newConfigName);
this.ultimateTimber.saveResource(newConfigName, false);
newConfigFile.renameTo(configFile);
}
@ -137,4 +161,4 @@ public class ConfigurationManager extends Manager {
return this.configuration;
}
}
}

View File

@ -18,6 +18,7 @@ public class HookManager extends Manager {
public HookManager(UltimateTimber ultimateTimber) {
super(ultimateTimber);
this.hooks = new HashSet<>();
}
@Override

View File

@ -0,0 +1,99 @@
package com.songoda.ultimatetimber.manager;
import com.songoda.ultimatetimber.UltimateTimber;
import com.songoda.ultimatetimber.adapter.IBlockData;
import com.songoda.ultimatetimber.tree.TreeDefinition;
import org.bukkit.Bukkit;
import org.bukkit.Location;
import org.bukkit.Material;
import org.bukkit.block.Block;
import java.util.HashSet;
import java.util.Random;
import java.util.Set;
public class SaplingManager extends Manager {
private Random random;
private Set<Location> protectedSaplings;
public SaplingManager(UltimateTimber ultimateTimber) {
super(ultimateTimber);
this.random = new Random();
this.protectedSaplings = new HashSet<>();
}
@Override
public void reload() {
}
@Override
public void disable() {
}
/**
* Replants a sapling given a TreeDefinition and Location
* Takes into account config settings
*
* @param treeDefinition The TreeDefinition of the sapling
* @param location The Location to plant the sapling
*/
public void replantSapling(TreeDefinition treeDefinition, Location location) {
if (!ConfigurationManager.Setting.REPLANT_SAPLINGS.getBoolean())
return;
this.internalReplant(treeDefinition, location);
}
/**
* Randomly replants a sapling given a TreeDefinition and Location
* Takes into account config settings
*
* @param treeDefinition The TreeDefinition of the sapling
* @param location The Location to plant the sapling
*/
public void replantSaplingWithChance(TreeDefinition treeDefinition, Location location) {
if (!ConfigurationManager.Setting.FALLING_BLOCKS_REPLANT_SAPLINGS.getBoolean())
return;
double chance = ConfigurationManager.Setting.FALLING_BLOCKS_REPLANT_SAPLINGS_CHANCE.getDouble();
if (this.random.nextDouble() > chance / 100)
return;
this.internalReplant(treeDefinition, location);
}
/**
* Replants a sapling given a TreeDefinition and Location
*
* @param treeDefinition The TreeDefinition of the sapling
* @param location The Location to plant the sapling
*/
private void internalReplant(TreeDefinition treeDefinition, Location location) {
Block block = location.getBlock();
if (!block.getType().equals(Material.AIR))
return;
IBlockData saplingBlockData = treeDefinition.getSaplingBlockData();
saplingBlockData.setBlock(location.getBlock());
int cooldown = ConfigurationManager.Setting.REPLANT_SAPLINGS_COOLDOWN.getInt();
if (cooldown != 0) {
this.protectedSaplings.add(location);
Bukkit.getScheduler().scheduleSyncDelayedTask(this.ultimateTimber, () -> this.protectedSaplings.remove(location), cooldown * 20L);
}
}
/**
* Gets if a sapling is protected
*
* @param block The Block to check
* @return True if the sapling is protected, otherwise false
*/
public boolean isSaplingProtected(Block block) {
return this.protectedSaplings.contains(block.getLocation());
}
}

View File

@ -1,7 +1,10 @@
package com.songoda.ultimatetimber.manager;
import com.songoda.ultimatetimber.UltimateTimber;
import com.songoda.ultimatetimber.tree.DetectedTree;
import com.songoda.ultimatetimber.tree.TreeDefinition;
import org.bukkit.Bukkit;
import org.bukkit.entity.Player;
import org.bukkit.event.EventHandler;
import org.bukkit.event.EventPriority;
import org.bukkit.event.Listener;
@ -24,6 +27,16 @@ public class TreeAnimationManager extends Manager implements Listener {
}
/**
* Plays an animation for toppling a tree
*
* @param detectedTree The DetectedTree
* @param player The Player who toppled the tree
*/
public void runAnimation(DetectedTree detectedTree, Player player) {
TreeDefinition treeDefinition = detectedTree.getTreeDefinition();
}
@EventHandler(priority = EventPriority.HIGH)
public void onFallingBlockLand(EntityChangeBlockEvent event) {

View File

@ -169,6 +169,8 @@ public class TreeDefinitionManager extends Manager {
* @return True if the tool is allowed for toppling any trees
*/
public boolean isToolValidForAnyTreeDefinition(ItemStack tool) {
if (ConfigurationManager.Setting.IGNORE_REQUIRED_TOOLS.getBoolean())
return true;
for (TreeDefinition treeDefinition : this.treeDefinitions)
for (ItemStack requiredTool : treeDefinition.getRequiredTools())
if (requiredTool.isSimilar(tool))
@ -187,6 +189,8 @@ public class TreeDefinitionManager extends Manager {
* @return True if the tool is allowed for toppling the given TreeDefinition
*/
public boolean isToolValidForTreeDefinition(TreeDefinition treeDefinition, ItemStack tool) {
if (ConfigurationManager.Setting.IGNORE_REQUIRED_TOOLS.getBoolean())
return true;
for (ItemStack requiredTool : treeDefinition.getRequiredTools())
if (requiredTool.isSimilar(tool))
return true;
@ -230,9 +234,10 @@ public class TreeDefinitionManager extends Manager {
}
// Roll the dice
double bonusLootMultiplier = ConfigurationManager.Setting.BONUS_LOOT_MULTIPLIER.getDouble();
for (TreeLoot treeLoot : toTry) {
double chance = hasBonusChance ? treeLoot.getChance() * 2 : treeLoot.getChance();
if (this.random.nextDouble() > chance)
if (this.random.nextDouble() > chance / 100)
continue;
if (treeLoot.hasItem())
lootedItems.add(treeLoot.getItem());
@ -252,7 +257,12 @@ public class TreeDefinitionManager extends Manager {
// Run looted commands
for (String lootedCommand : lootedCommands)
Bukkit.getServer().dispatchCommand(Bukkit.getConsoleSender(), lootedCommand.replace("%player", player.getName()).replace("%type", treeDefinition.getKey()));
Bukkit.getServer().dispatchCommand(Bukkit.getConsoleSender(),
lootedCommand.replace("%player", player.getName())
.replace("%type", treeDefinition.getKey())
.replace("%xPos", treeBlock.getLocation().getBlockX() + "")
.replace("%yPos", treeBlock.getLocation().getBlockY() + "")
.replace("%zPos", treeBlock.getLocation().getBlockZ() + ""));
}
/**

View File

@ -2,7 +2,6 @@ package com.songoda.ultimatetimber.manager;
import com.songoda.ultimatetimber.UltimateTimber;
import com.songoda.ultimatetimber.adapter.IBlockData;
import com.songoda.ultimatetimber.adapter.VersionAdapter;
import com.songoda.ultimatetimber.tree.*;
import org.bukkit.block.Block;
import org.bukkit.block.BlockFace;
@ -19,7 +18,6 @@ public class TreeDetectionManager extends Manager {
private TreeDefinitionManager treeDefinitionManager;
private int maxBranchBlocksAllowed;
private int numLeavesRequiredForTree;
private boolean allowMixedTreeTypes;
private boolean onlyBreakLogsUpwards;
private boolean destroyBaseLog;
private boolean entireTreeBase;
@ -27,27 +25,27 @@ public class TreeDetectionManager extends Manager {
public TreeDetectionManager(UltimateTimber ultimateTimber) {
super(ultimateTimber);
VALID_BRANCH_OFFSETS = new HashSet<>();
VALID_TRUNK_OFFSETS = new HashSet<>();
VALID_LEAF_OFFSETS = new HashSet<>();
this.VALID_BRANCH_OFFSETS = new HashSet<>();
this.VALID_TRUNK_OFFSETS = new HashSet<>();
this.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));
this.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));
this.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.VALID_LEAF_OFFSETS.add(new Vector(i, 0, 0));
this.VALID_LEAF_OFFSETS.add(new Vector(0, i, 0));
this.VALID_LEAF_OFFSETS.add(new Vector(0, 0, i));
}
}
@ -56,7 +54,6 @@ public class TreeDetectionManager extends Manager {
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();
@ -76,7 +73,10 @@ public class TreeDetectionManager extends Manager {
public DetectedTree detectTree(Block initialBlock) {
TreeBlock initialTreeBlock = new TreeBlock(initialBlock, TreeBlockType.LOG);
TreeBlockSet<Block> detectedTreeBlocks = new TreeBlockSet<>(initialTreeBlock);
Set<TreeDefinition> possibleTreeDefinitions = treeDefinitionManager.getTreeDefinitionsForLog(initialBlock);
Set<TreeDefinition> possibleTreeDefinitions = this.treeDefinitionManager.getTreeDefinitionsForLog(initialBlock);
if (possibleTreeDefinitions.isEmpty())
return null;
// Detect tree trunk
Set<Block> trunkBlocks = new HashSet<>();
@ -151,7 +151,7 @@ public class TreeDetectionManager extends Manager {
if (treeBlocks.size() > this.maxBranchBlocksAllowed)
return;
for (Vector offset : this.onlyBreakLogsUpwards ? VALID_BRANCH_OFFSETS : VALID_TRUNK_OFFSETS) {
for (Vector offset : this.onlyBreakLogsUpwards ? this.VALID_BRANCH_OFFSETS : this.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)) {
@ -178,7 +178,7 @@ public class TreeDetectionManager extends Manager {
if (distanceFromLog > maxDistanceFromLog)
return;
for (Vector offset : !detectLeavesDiagonally ? VALID_LEAF_OFFSETS : VALID_TRUNK_OFFSETS) {
for (Vector offset : !detectLeavesDiagonally ? this.VALID_LEAF_OFFSETS : this.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)) {
@ -200,7 +200,7 @@ public class TreeDetectionManager extends Manager {
* @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) {
for (Vector offset : this.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;
@ -216,7 +216,6 @@ public class TreeDetectionManager extends Manager {
* @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)
for (IBlockData logBlockData : treeDefinition.getLogBlockData())
if (logBlockData.isSimilar(block))
@ -232,7 +231,6 @@ public class TreeDetectionManager extends Manager {
* @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)
for (IBlockData leafBlockData : treeDefinition.getLeafBlockData())
if (leafBlockData.isSimilar(block))

View File

@ -1,11 +1,19 @@
package com.songoda.ultimatetimber.manager;
import com.songoda.ultimatetimber.UltimateTimber;
import com.songoda.ultimatetimber.adapter.VersionAdapter;
import com.songoda.ultimatetimber.events.TreeFallEvent;
import com.songoda.ultimatetimber.events.TreeFellEvent;
import com.songoda.ultimatetimber.tree.DetectedTree;
import org.bukkit.Bukkit;
import org.bukkit.GameMode;
import org.bukkit.block.Block;
import org.bukkit.entity.Player;
import org.bukkit.event.EventHandler;
import org.bukkit.event.EventPriority;
import org.bukkit.event.Listener;
import org.bukkit.event.block.BlockBreakEvent;
import org.bukkit.inventory.ItemStack;
public class TreeFallManager extends Manager implements Listener {
@ -26,7 +34,70 @@ public class TreeFallManager extends Manager implements Listener {
@EventHandler(priority = EventPriority.LOW, ignoreCancelled = true)
public void onBlockBreak(BlockBreakEvent event) {
TreeDefinitionManager treeDefinitionManager = this.ultimateTimber.getTreeDefinitionManager();
TreeDetectionManager treeDetectionManager = this.ultimateTimber.getTreeDetectionManager();
TreeAnimationManager treeAnimationManager = this.ultimateTimber.getTreeAnimationManager();
ChoppingManager choppingManager = this.ultimateTimber.getChoppingManager();
SaplingManager saplingManager = this.ultimateTimber.getSaplingManager();
VersionAdapter versionAdapter = this.ultimateTimber.getVersionAdapter();
HookManager hookManager = this.ultimateTimber.getHookManager();
Player player = event.getPlayer();
Block block = event.getBlock();
ItemStack tool = versionAdapter.getItemInHand(player);
// Protect saplings
if (saplingManager.isSaplingProtected(block)) {
event.setCancelled(true);
return;
}
// Condition checks
if (ConfigurationManager.Setting.DISABLED_WORLDS.getStringList().contains(player.getWorld().getName()))
return;
if (!ConfigurationManager.Setting.ALLOW_CREATIVE_MODE.getBoolean() && player.getGameMode().equals(GameMode.CREATIVE))
return;
if (ConfigurationManager.Setting.ONLY_TOPPLE_WHILE_SNEAKING.getBoolean() && !player.isSneaking())
return;
if (ConfigurationManager.Setting.REQUIRE_CHOP_PERMISSION.getBoolean() && !player.hasPermission("ultimatetimber.chop"))
return;
if (!choppingManager.isChopping(player))
return;
if (!treeDefinitionManager.isToolValidForAnyTreeDefinition(tool))
return;
DetectedTree detectedTree = treeDetectionManager.detectTree(block);
if (detectedTree == null)
return;
int toolDamage = ConfigurationManager.Setting.REALISTIC_TOOL_DAMAGE.getBoolean() ? detectedTree.getDetectedTreeBlocks().getLogBlocks().size() : 1;
if (ConfigurationManager.Setting.PROTECT_TOOL.getBoolean() && !versionAdapter.hasEnoughDurability(tool, toolDamage))
return;
// Trigger fall event
TreeFallEvent treeFallEvent = new TreeFallEvent(player, detectedTree);
Bukkit.getPluginManager().callEvent(treeFallEvent);
if (treeFallEvent.isCancelled())
return;
// Valid tree and meets all conditions past this point
event.setCancelled(true);
if (!player.getGameMode().equals(GameMode.CREATIVE)) {
versionAdapter.applyToolDurability(tool, toolDamage);
hookManager.applyHooks(player, detectedTree.getDetectedTreeBlocks());
}
treeAnimationManager.runAnimation(detectedTree, player);
// Trigger fell event
TreeFellEvent treeFellEvent = new TreeFellEvent(player, detectedTree);
Bukkit.getPluginManager().callEvent(treeFellEvent);
}
}

View File

@ -1,303 +0,0 @@
package com.songoda.ultimatetimber.old_code;
import com.songoda.ultimatetimber.UltimateTimber;
import com.songoda.ultimatetimber.utils.LogToLeafConverter;
import org.bukkit.Material;
import org.bukkit.block.Block;
import org.bukkit.block.BlockFace;
import org.bukkit.configuration.file.FileConfiguration;
import org.bukkit.util.Vector;
import java.util.Arrays;
import java.util.Comparator;
import java.util.HashSet;
import java.util.Set;
public class TreeChecker {
/*
Used to check if a piece of wood is a part of the tree
*/
private final static Set<Material> VALID_LOG_MATERIALS = new HashSet<>(Arrays.asList(
Material.ACACIA_LOG,
Material.ACACIA_WOOD,
Material.STRIPPED_ACACIA_LOG,
Material.STRIPPED_ACACIA_WOOD,
Material.BIRCH_LOG,
Material.BIRCH_WOOD,
Material.STRIPPED_BIRCH_LOG,
Material.STRIPPED_BIRCH_WOOD,
Material.DARK_OAK_LOG,
Material.DARK_OAK_WOOD,
Material.STRIPPED_DARK_OAK_LOG,
Material.STRIPPED_DARK_OAK_WOOD,
Material.JUNGLE_LOG,
Material.JUNGLE_WOOD,
Material.STRIPPED_JUNGLE_LOG,
Material.STRIPPED_JUNGLE_WOOD,
Material.OAK_LOG,
Material.OAK_WOOD,
Material.STRIPPED_OAK_LOG,
Material.STRIPPED_OAK_WOOD,
Material.SPRUCE_LOG,
Material.SPRUCE_WOOD,
Material.STRIPPED_SPRUCE_LOG,
Material.STRIPPED_SPRUCE_WOOD,
Material.MUSHROOM_STEM
));
/*
Used to check if a leaf is a part of the tree
*/
private final static Set<Material> VALID_LEAF_MATERIALS = new HashSet<>(Arrays.asList(
Material.ACACIA_LEAVES,
Material.BIRCH_LEAVES,
Material.DARK_OAK_LEAVES,
Material.JUNGLE_LEAVES,
Material.OAK_LEAVES,
Material.SPRUCE_LEAVES,
Material.BROWN_MUSHROOM_BLOCK,
Material.RED_MUSHROOM_BLOCK
));
/**
* Gets a Set of all valid wood materials
*
* @return A Set of all valid wood materials
*/
public static Set<Material> getValidWoodMaterials() {
return VALID_LOG_MATERIALS;
}
private static final Set<Vector> VALID_TRUNK_OFFSETS, VALID_BRANCH_OFFSETS, VALID_LEAF_OFFSETS;
private HashSet<Block> treeBlocks;
private int maxDistanceFromLog;
private Material logType, leafType;
private int startingBlockY;
private int maxBranchBlocksAllowed;
private int numLeavesRequiredForTree;
private boolean allowMixedTreeTypes;
private boolean onlyBreakLogsUpwards;
private boolean destroyBaseLog;
private boolean entireTreeBase;
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));
}
}
/**
* Parses a block for a potential tree
*
* @param block The based block of the potential tree
* @return A HashSet of all blocks in the tree, or null if no tree was found
*/
protected HashSet<Block> parseTree(Block block) {
this.treeBlocks = new HashSet<>();
this.treeBlocks.add(block);
// Set tree information
this.logType = block.getType();
this.leafType = LogToLeafConverter.convert(this.logType);
this.startingBlockY = block.getLocation().getBlockY();
this.isMushroom = this.logType.equals(Material.MUSHROOM_STEM);
// Load settings for algorithm
FileConfiguration config = UltimateTimber.getInstance().getConfig();
this.allowMixedTreeTypes = config.getBoolean(DefaultConfig.ALLOW_MIXED_TREE_TYPES);
this.maxBranchBlocksAllowed = config.getInt(DefaultConfig.MAX_BRANCH_BLOCKS);
this.numLeavesRequiredForTree = config.getInt(DefaultConfig.LEAVES_FOR_TREE);
this.onlyBreakLogsUpwards = config.getBoolean(DefaultConfig.ONLY_BREAK_LOGS_UPWARDS);
this.destroyBaseLog = config.getBoolean(DefaultConfig.DELETE_BROKEN_LOG);
this.entireTreeBase = config.getBoolean(DefaultConfig.ENTIRE_TREE_BASE);
// 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);
}
// 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;
// The lowest logs of the tree must not have a plantable surface below them
if (this.entireTreeBase) {
int lowestY = this.treeBlocks.stream().min(Comparator.comparingInt(Block::getY)).get().getY();
boolean isTreeGrounded = this.treeBlocks.stream().filter(x -> x.getY() == lowestY).anyMatch(x -> {
Material typeBelow = x.getRelative(BlockFace.DOWN).getType();
return (typeBelow.equals(Material.DIRT) ||
typeBelow.equals(Material.COARSE_DIRT) ||
typeBelow.equals(Material.PODZOL) ||
typeBelow.equals(Material.GRASS_BLOCK) ||
isValidLogType(typeBelow)) &&
!x.equals(block) &&
isValidLogType(x.getType());
});
if (isTreeGrounded)
return null;
}
// Delete the starting block if applicable
if (this.destroyBaseLog)
this.treeBlocks.remove(block);
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())) {
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);
}
/**
* 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

@ -159,6 +159,7 @@ public class TreeBlockSet<BlockType> implements Collection {
}
@Override
@SuppressWarnings("unchecked")
public Object[] toArray(Object[] a) {
Set<ITreeBlock<BlockType>> treeBlocks = new HashSet<>();
for (Object o : a)

View File

@ -58,7 +58,7 @@ public class TreeLoot {
* @return The command that this tree loot can run
*/
public String getCommand() {
return command;
return this.command;
}
/**

View File

@ -1,8 +1,10 @@
package com.songoda.ultimatetimber.tree.animation;
import com.songoda.ultimatetimber.tree.DetectedTree;
import com.songoda.ultimatetimber.tree.TreeBlockSet;
import com.songoda.ultimatetimber.tree.TreeDefinition;
import org.bukkit.block.Block;
import org.bukkit.entity.Player;
public abstract class TreeAnimation {
@ -16,6 +18,12 @@ public abstract class TreeAnimation {
this.treeDefinition = treeDefinition;
}
abstract void playAnimation();
/**
* Plays this tree topple animation
*
* @param detectedTree The DetectedTree
* @param player The Player who toppled the tree
*/
abstract void playAnimation(DetectedTree detectedTree, Player player);
}

View File

@ -1,8 +1,10 @@
package com.songoda.ultimatetimber.tree.animation;
import com.songoda.ultimatetimber.tree.DetectedTree;
import com.songoda.ultimatetimber.tree.TreeBlockSet;
import com.songoda.ultimatetimber.tree.TreeDefinition;
import org.bukkit.block.Block;
import org.bukkit.entity.Player;
public class TreeAnimationChaos extends TreeAnimation {
@ -11,7 +13,7 @@ public class TreeAnimationChaos extends TreeAnimation {
}
@Override
public void playAnimation() {
public void playAnimation(DetectedTree detectedTree, Player player) {
}

View File

@ -1,8 +1,10 @@
package com.songoda.ultimatetimber.tree.animation;
import com.songoda.ultimatetimber.tree.DetectedTree;
import com.songoda.ultimatetimber.tree.TreeBlockSet;
import com.songoda.ultimatetimber.tree.TreeDefinition;
import org.bukkit.block.Block;
import org.bukkit.entity.Player;
public class TreeAnimationDisintegrate extends TreeAnimation {
@ -11,7 +13,7 @@ public class TreeAnimationDisintegrate extends TreeAnimation {
}
@Override
public void playAnimation() {
public void playAnimation(DetectedTree detectedTree, Player player) {
}

View File

@ -1,8 +1,10 @@
package com.songoda.ultimatetimber.tree.animation;
import com.songoda.ultimatetimber.tree.DetectedTree;
import com.songoda.ultimatetimber.tree.TreeBlockSet;
import com.songoda.ultimatetimber.tree.TreeDefinition;
import org.bukkit.block.Block;
import org.bukkit.entity.Player;
public class TreeAnimationFancy extends TreeAnimation {
@ -11,7 +13,7 @@ public class TreeAnimationFancy extends TreeAnimation {
}
@Override
public void playAnimation() {
public void playAnimation(DetectedTree detectedTree, Player player) {
}

View File

@ -1,8 +1,10 @@
package com.songoda.ultimatetimber.tree.animation;
import com.songoda.ultimatetimber.tree.DetectedTree;
import com.songoda.ultimatetimber.tree.TreeBlockSet;
import com.songoda.ultimatetimber.tree.TreeDefinition;
import org.bukkit.block.Block;
import org.bukkit.entity.Player;
public class TreeAnimationNone extends TreeAnimation {
@ -11,7 +13,7 @@ public class TreeAnimationNone extends TreeAnimation {
}
@Override
public void playAnimation() {
public void playAnimation(DetectedTree detectedTree, Player player) {
}

View File

@ -692,4 +692,4 @@ public class Metrics {
}
}
}
}

View File

@ -19,4 +19,4 @@ public class NMSUtil {
return Integer.valueOf(NMSVersion.substring(NMSVersion.length() - 2).replace(".", ""));
}
}
}

View File

@ -24,9 +24,15 @@ max-logs-per-chop: 120
leaves-required-for-tree: 5
# Apply realistic damage to the tools based on the number of logs chopped
# If false, only one durability will be removed from the tool
# Default: true
realistic-tool-damage: true
# Protect the tool used to chop down the tree from breaking
# Prevents the tree from being toppled if the tool would break
# Default: false
protect-tool: false
# Require the entire base of the tree to be broken before it topples
# Default: false
break-entire-tree-base: false
@ -62,8 +68,8 @@ replant-saplings: true
# How many seconds to prevent players from breaking replanted saplings
# Set to 0 to disable
# Does nothing if replant-saplings is false
# Default: 5
replant-saplings-cooldown: 5
# Default: 3
replant-saplings-cooldown: 3
# Give fallen leaf blocks a chance to replant saplings when they hit the ground
# Default: true
@ -71,6 +77,7 @@ falling-blocks-replant-saplings: true
# The percent chance that fallen leaves have of planting a sapling
# Does nothing if falling-blocks-replant-saplings is false
# The chance is out of 100 and may contain decimals
# Default: 1
falling-blocks-replant-saplings-chance: 1
@ -95,6 +102,12 @@ use-custom-sounds: true
# Default: true
use-custom-particles: true
# The bonus loot multiplier when a player has the permission ultimatetimber.bonusloot
# Multiplies the chance of tree drops by this value
# Decimal values are allowed
# Default: 2
bonus-loot-multiplier: 2
# The type of animation to use for tree toppling
# Types: FANCY, DISINTEGRATE, CHAOS, NONE
tree-animation-type: FANCY
@ -283,6 +296,7 @@ global-plantable-soil:
- GRASS_BLOCK
- DIRT
- COARSE_DIRT
- PODZOL
# Custom loot that is available for all tree types
# The loot applies to each log broken in the tree
@ -298,7 +312,7 @@ global-log-loot:
chance: 0
2:
material: GOLDEN_APPLE
command: 'broadcast %player found a golden apple in a %type tree!'
command: 'broadcast %player found a golden apple in a %type tree at %xPos %yPos %zPos!'
chance: 0
# Custom loot that is available for all tree types

View File

@ -25,9 +25,15 @@ max-logs-per-chop: 120
leaves-required-for-tree: 5
# Apply realistic damage to the tools based on the number of logs chopped
# If false, only one durability will be removed from the tool
# Default: true
realistic-tool-damage: true
# Protect the tool used to chop down the tree from breaking
# Prevents the tree from being toppled if the tool would break
# Default: false
protect-tool: false
# Require the entire base of the tree to be broken before it topples
# Default: false
break-entire-tree-base: false
@ -63,8 +69,8 @@ replant-saplings: true
# How many seconds to prevent players from breaking replanted saplings
# Set to 0 to disable
# Does nothing if replant-saplings is false
# Default: 5
replant-saplings-cooldown: 5
# Default: 3
replant-saplings-cooldown: 3
# Give fallen leaf blocks a chance to replant saplings when they hit the ground
# Default: true
@ -72,6 +78,7 @@ falling-blocks-replant-saplings: true
# The percent chance that fallen leaves have of planting a sapling
# Does nothing if falling-blocks-replant-saplings is false
# The chance is out of 100 and may contain decimals
# Default: 1
falling-blocks-replant-saplings-chance: 1
@ -96,6 +103,12 @@ use-custom-sounds: true
# Default: true
use-custom-particles: true
# The bonus loot multiplier when a player has the permission ultimatetimber.bonusloot
# Multiplies the chance of tree drops by this value
# Decimal values are allowed
# Default: 2
bonus-loot-multiplier: 2
# The type of animation to use for tree toppling
# Types: FANCY, DISINTEGRATE, CHAOS, NONE
tree-animation-type: FANCY
@ -323,6 +336,7 @@ global-plantable-soil:
- GRASS
- DIRT:0
- DIRT:1
- DIRT:2
# Custom loot that is available for all tree types
# The loot applies to each log broken in the tree
@ -338,7 +352,7 @@ global-log-loot:
chance: 0
2:
material: GOLDEN_APPLE
command: 'broadcast %player found a golden apple in a %type tree!'
command: 'broadcast %player found a golden apple in a %type tree at %xPos %yPos %zPos!'
chance: 0
# Custom loot that is available for all tree types

View File

@ -24,4 +24,4 @@ permissions:
default: op
ultimatetimber.reload:
description: Reloads the configuration file
default: op
default: op