diff --git a/src/main/java/com/gmail/nossr50/listeners/BlockListener.java b/src/main/java/com/gmail/nossr50/listeners/BlockListener.java index 55dee4b61..eea4fc480 100644 --- a/src/main/java/com/gmail/nossr50/listeners/BlockListener.java +++ b/src/main/java/com/gmail/nossr50/listeners/BlockListener.java @@ -186,7 +186,7 @@ public class BlockListener implements Listener { WoodCutting.woodcuttingBlockCheck(player, block); } - if (profile.getAbilityMode(AbilityType.TREE_FELLER) && Permissions.treeFeller(player) && ItemChecks.isAxe(inHand)) { + if (!mcMMO.placeStore.isTrue(block) && profile.getAbilityMode(AbilityType.TREE_FELLER) && Permissions.treeFeller(player) && ItemChecks.isAxe(inHand)) { WoodCutting.treeFeller(event); } } diff --git a/src/main/java/com/gmail/nossr50/skills/gathering/WoodCutting.java b/src/main/java/com/gmail/nossr50/skills/gathering/WoodCutting.java index 07d0ae4b6..b78f6ba2c 100644 --- a/src/main/java/com/gmail/nossr50/skills/gathering/WoodCutting.java +++ b/src/main/java/com/gmail/nossr50/skills/gathering/WoodCutting.java @@ -1,12 +1,14 @@ package com.gmail.nossr50.skills.gathering; import java.util.ArrayList; +import java.util.List; import org.bukkit.Location; import org.bukkit.Material; import org.bukkit.TreeSpecies; +import org.bukkit.World; import org.bukkit.block.Block; -import org.bukkit.enchantments.Enchantment; +import org.bukkit.block.BlockFace; import org.bukkit.entity.Player; import org.bukkit.event.block.BlockBreakEvent; import org.bukkit.inventory.ItemStack; @@ -17,7 +19,6 @@ import org.getspout.spoutapi.sound.SoundEffect; import com.gmail.nossr50.mcMMO; import com.gmail.nossr50.config.AdvancedConfig; import com.gmail.nossr50.config.Config; -import com.gmail.nossr50.datatypes.PlayerProfile; import com.gmail.nossr50.datatypes.SkillType; import com.gmail.nossr50.datatypes.mods.CustomBlock; import com.gmail.nossr50.events.fake.FakePlayerAnimationEvent; @@ -32,317 +33,217 @@ import com.gmail.nossr50.util.Skills; import com.gmail.nossr50.util.Users; public class WoodCutting { - - static AdvancedConfig advancedConfig = AdvancedConfig.getInstance(); + private static final AdvancedConfig ADVANCED_CONFIG = AdvancedConfig.getInstance(); + private static boolean treeFellerReachedThreshold = false; /** * Handle the Tree Feller ability. * - * @param event Event to modify + * @param event Event to process */ public static void treeFeller(BlockBreakEvent event) { - Player player = event.getPlayer(); - Block firstBlock = event.getBlock(); - PlayerProfile profile = Users.getProfile(player); - ArrayList toBeFelled = new ArrayList(); + List toBeFelled = processTreeFeller(event); - /* NOTE: Tree Feller will cut upwards like how you actually fell trees */ - processTreeFelling(firstBlock, toBeFelled); - removeBlocks(toBeFelled, player, profile); + if (toBeFelled != null && !toBeFelled.isEmpty()) { + removeBlocks(toBeFelled, event.getPlayer()); + } } /** * Handles removing & dropping the blocks from Tree Feller. * - * @param toBeFelled List of Blocks to be removed from the tree - * @param player The player using the ability - * @param profile The PlayerProfile of the player + * @param toBeFelled List of blocks to be removed + * @param player Player using the ability */ - private static void removeBlocks(ArrayList toBeFelled, Player player, PlayerProfile profile) { - if (toBeFelled.size() >= Config.getInstance().getTreeFellerThreshold()) { - player.sendMessage(LocaleLoader.getString("Woodcutting.Skills.TreeFellerThreshold")); - return; - } - - int xp = 0; + private static void removeBlocks(List toBeFelled, Player player) { ItemStack inHand = player.getItemInHand(); - int level = 0; - if (inHand.containsEnchantment(Enchantment.DURABILITY)) { - level = inHand.getEnchantmentLevel(Enchantment.DURABILITY); - } - int durabilityLoss = durabilityLossCalulate(toBeFelled, level); + Material inHandMaterial = inHand.getType(); + short finalDurability = (short) (inHand.getDurability() + calulateDurabilityLossFromTreeFeller(toBeFelled)); - /* This is to prevent using wood axes everytime you tree fell */ - if (ModChecks.isCustomTool(inHand)) { - if (inHand.getDurability() + durabilityLoss >= ModChecks.getToolFromItemStack(inHand).getDurability()) { + // Prevent the tree to be cut down if the tool doesn't have enough durability + if (inHandMaterial != Material.AIR) { + short maxDurability = ModChecks.isCustomTool(inHand) ? ModChecks.getToolFromItemStack(inHand).getDurability() : inHandMaterial.getMaxDurability(); + + if (finalDurability >= maxDurability) { player.sendMessage(LocaleLoader.getString("Woodcutting.Skills.TreeFeller.Splinter")); int health = player.getHealth(); if (health >= 2) { - Combat.dealDamage(player, Misc.getRandom().nextInt(health - 1)); + Combat.dealDamage(player, Misc.getRandom().nextInt(health - 1)); // Why not base the damage on the number of elements in toBeFelled? } - inHand.setDurability(inHand.getType().getMaxDurability()); + + inHand.setDurability(maxDurability); return; } } - else if ((inHand.getDurability() + durabilityLoss >= inHand.getType().getMaxDurability()) || inHand.getType().equals(Material.AIR)) { - player.sendMessage(LocaleLoader.getString("Woodcutting.Skills.TreeFeller.Splinter")); - int health = player.getHealth(); + inHand.setDurability(finalDurability); - if (health >= 2) { - Combat.dealDamage(player, Misc.getRandom().nextInt(health - 1)); - } - inHand.setDurability(inHand.getType().getMaxDurability()); - return; - } - - /* Damage the tool */ - inHand.setDurability((short) (inHand.getDurability() + durabilityLoss)); - - //Prepare ItemStacks + int xp = 0; ItemStack item = null; - ItemStack oak = (new MaterialData(Material.LOG, TreeSpecies.GENERIC.getData())).toItemStack(1); - ItemStack spruce = (new MaterialData(Material.LOG, TreeSpecies.REDWOOD.getData())).toItemStack(1); - ItemStack birch = (new MaterialData(Material.LOG, TreeSpecies.BIRCH.getData())).toItemStack(1); - ItemStack jungle = (new MaterialData(Material.LOG, TreeSpecies.JUNGLE.getData())).toItemStack(1); - for (Block x : toBeFelled) { - if (Misc.blockBreakSimulate(x, player, true)) { - if (Config.getInstance().getBlockModsEnabled() && ModChecks.isCustomLogBlock(x)) { - if (ModChecks.isCustomLogBlock(x)) { - CustomBlock block = ModChecks.getCustomBlock(x); - item = block.getItemDrop(); + for (Block block : toBeFelled) { + if (!Misc.blockBreakSimulate(block, player, true)) { + break; + } - if (!mcMMO.placeStore.isTrue(x)) { - WoodCutting.woodCuttingProcCheck(player, x); - xp = block.getXpGain(); - } + if (block.getType() == Material.LOG) { + WoodCutting.woodCuttingProcCheck(player, block); - /* Remove the block */ - x.setData((byte) 0x0); - x.setType(Material.AIR); + TreeSpecies species = ((Tree) block.getState().getData()).getSpecies(); - int minimumDropAmount = block.getMinimumDropAmount(); - int maximumDropAmount = block.getMaximumDropAmount(); + switch (species) { + case GENERIC: + item = new MaterialData(Material.LOG, TreeSpecies.GENERIC.getData()).toItemStack(1); + xp += Config.getInstance().getWoodcuttingXPOak(); + break; + case REDWOOD: + item = new MaterialData(Material.LOG, TreeSpecies.REDWOOD.getData()).toItemStack(1); + xp += Config.getInstance().getWoodcuttingXPSpruce(); + break; + case BIRCH: + item = new MaterialData(Material.LOG, TreeSpecies.BIRCH.getData()).toItemStack(1); + xp += Config.getInstance().getWoodcuttingXPBirch(); + break; + case JUNGLE: + item = new MaterialData(Material.LOG, TreeSpecies.JUNGLE.getData()).toItemStack(1); + xp += Config.getInstance().getWoodcuttingXPJungle() / 2; // Nerf XP from Jungle Trees when using Tree Feller + break; + default: + break; + } - item = block.getItemDrop(); + Misc.dropItem(block.getLocation(), item); + } + else if (block.getType() == Material.LEAVES) { + item = new MaterialData(Material.SAPLING, (byte) (block.getData() & 3)).toItemStack(1); - if (minimumDropAmount != maximumDropAmount) { - Misc.dropItems(x.getLocation(), item, minimumDropAmount); - Misc.randomDropItems(x.getLocation(), item, 50, maximumDropAmount - minimumDropAmount); - } - else { - Misc.dropItems(x.getLocation(), item, minimumDropAmount); - } - } - else if (ModChecks.isCustomLeafBlock(x)) { - CustomBlock block = ModChecks.getCustomBlock(x); - item = block.getItemDrop(); + Misc.randomDropItem(block.getLocation(), item, 10); + } + else if (Config.getInstance().getBlockModsEnabled()) { + if (ModChecks.isCustomLogBlock(block)) { + CustomBlock customBlock = ModChecks.getCustomBlock(block); - final int SAPLING_DROP_CHANCE = 10; + WoodCutting.woodCuttingProcCheck(player, block); - /* Remove the block */ - x.setData((byte) 0x0); - x.setType(Material.AIR); + xp = customBlock.getXpGain(); + int minimumDropAmount = customBlock.getMinimumDropAmount(); + int maximumDropAmount = customBlock.getMaximumDropAmount(); + Location location = block.getLocation(); + item = customBlock.getItemDrop(); - Misc.randomDropItem(x.getLocation(), item, SAPLING_DROP_CHANCE); + Misc.dropItems(location, item, minimumDropAmount); + + if (minimumDropAmount < maximumDropAmount) { + Misc.randomDropItems(location, item, 50, maximumDropAmount - minimumDropAmount); } } - else if (x.getType() == Material.LOG) { - Tree tree = (Tree) x.getState().getData(); - TreeSpecies species = tree.getSpecies(); + else if (ModChecks.isCustomLeafBlock(block)) { + CustomBlock customBlock = ModChecks.getCustomBlock(block); - switch (species) { - case GENERIC: - item = oak; - break; - - case REDWOOD: - item = spruce; - break; - - case BIRCH: - item = birch; - break; - - case JUNGLE: - item = jungle; - break; - - default: - break; - } - - if (!mcMMO.placeStore.isTrue(x)) { - WoodCutting.woodCuttingProcCheck(player, x); - - switch (species) { - case GENERIC: - xp += Config.getInstance().getWoodcuttingXPOak(); - break; - - case REDWOOD: - xp += Config.getInstance().getWoodcuttingXPSpruce(); - break; - - case BIRCH: - xp += Config.getInstance().getWoodcuttingXPBirch(); - break; - - case JUNGLE: - xp += Config.getInstance().getWoodcuttingXPJungle() / 2; //Nerf XP from Jungle Trees when using Tree Feller - break; - - default: - break; - } - } - - /* Remove the block */ - x.setData((byte) 0x0); - x.setType(Material.AIR); - - /* Drop the block */ - Misc.dropItem(x.getLocation(), item); - } - else if (x.getType() == Material.LEAVES) { - final int SAPLING_DROP_CHANCE = 10; - - //Drop the right type of sapling - item = (new MaterialData(Material.SAPLING, (byte) (x.getData() & 3))).toItemStack(1); - - Misc.randomDropItem(x.getLocation(), item, SAPLING_DROP_CHANCE); - - //Remove the block - x.setData((byte) 0); - x.setType(Material.AIR); + Misc.randomDropItem(block.getLocation(), customBlock.getItemDrop(), 10); } } + + block.setData((byte) 0); + block.setType(Material.AIR); } if (Permissions.woodcutting(player)) { - Skills.xpProcessing(player, profile, SkillType.WOODCUTTING, xp); + Skills.xpProcessing(player, Users.getProfile(player), SkillType.WOODCUTTING, xp); } } /** - * Handle the calculations from Tree Feller. + * Process Tree Feller around a block. * - * @param currentBlock The current block to be removed - * @param toBeFelled The list of blocks left to be removed + * @param block Point of origin of the layer + * @param toBeFelled List of blocks to be removed */ - private static void processTreeFelling(Block currentBlock, ArrayList toBeFelled) { - Material type = currentBlock.getType(); + private static void processTreeFellerAroundBlock(Block block, List toBeFelled) { + // TODO: too much duplicate code here + List futureCenterBlocks = new ArrayList(); + boolean centerIsLog = (block.getType() == Material.LOG); //TODO: custom blocks? + + // Handle the block above 'block' + Block nextBlock = block.getRelative(BlockFace.UP);; - if (toBeFelled.size() >= Config.getInstance().getTreeFellerThreshold()) { - return; - } + if (BlockChecks.treeFellerCompatible(nextBlock) && !toBeFelled.contains(nextBlock) && !mcMMO.placeStore.isTrue(nextBlock)) { + toBeFelled.add(nextBlock); - if (!mcMMO.placeStore.isTrue(currentBlock)) { - if ((type.equals(Material.LOG) || type.equals(Material.LEAVES))) { - toBeFelled.add(currentBlock); + if (centerIsLog) { + futureCenterBlocks.add(nextBlock); } - else if (Config.getInstance().getBlockModsEnabled() && (ModChecks.isCustomLogBlock(currentBlock) || ModChecks.isCustomLeafBlock(currentBlock))) { - toBeFelled.add(currentBlock); + + if (toBeFelled.size() >= Config.getInstance().getTreeFellerThreshold()) { + treeFellerReachedThreshold = true; + return; } } - Block xPositive = currentBlock.getRelative(1, 0, 0); - Block xNegative = currentBlock.getRelative(-1, 0, 0); - Block zPositive = currentBlock.getRelative(0, 0, 1); - Block zNegative = currentBlock.getRelative(0, 0, -1); - Block yPositive = currentBlock.getRelative(0, 1, 0); + World world = block.getWorld(); - if (!mcMMO.placeStore.isTrue(currentBlock)) { - if (!isTooAggressive(currentBlock, xPositive) && BlockChecks.treeFellerCompatible(xPositive) && !toBeFelled.contains(xPositive)) { - processTreeFelling(xPositive, toBeFelled); - } + // Handle the blocks around 'block' + for (int x = -1 ; x <= 1 ; x++) { + for (int z = -1 ; z <= 1 ; z++) { + nextBlock = world.getBlockAt(block.getLocation().add(x, 0, z)); - if (!isTooAggressive(currentBlock, xNegative) && BlockChecks.treeFellerCompatible(xNegative) && !toBeFelled.contains(xNegative)) { - processTreeFelling(xNegative, toBeFelled); - } + if (BlockChecks.treeFellerCompatible(nextBlock) && !toBeFelled.contains(nextBlock) && !mcMMO.placeStore.isTrue(nextBlock)) { + toBeFelled.add(nextBlock); - if (!isTooAggressive(currentBlock, zPositive) && BlockChecks.treeFellerCompatible(zPositive) && !toBeFelled.contains(zPositive)) { - processTreeFelling(zPositive, toBeFelled); - } + if (centerIsLog) { + futureCenterBlocks.add(nextBlock); + } - if (!isTooAggressive(currentBlock, zNegative) && BlockChecks.treeFellerCompatible(zNegative) && !toBeFelled.contains(zNegative)) { - processTreeFelling(zNegative, toBeFelled); - } - } - - byte data = currentBlock.getData(); - - if ((data & 0x4) == 0x4) - data ^= 0x4; - - if ((data & 0x8) == 0x8) - data ^= 0x8; - - if (TreeSpecies.getByData(data) == TreeSpecies.JUNGLE) { - Block corner1 = currentBlock.getRelative(1, 0, 1); - Block corner2 = currentBlock.getRelative(1, 0, -1); - Block corner3 = currentBlock.getRelative(-1, 0, 1); - Block corner4 = currentBlock.getRelative(-1, 0, -1); - - if (!mcMMO.placeStore.isTrue(currentBlock)) { - if (!isTooAggressive(currentBlock, corner1) && BlockChecks.treeFellerCompatible(corner1) && !toBeFelled.contains(corner1)) { - processTreeFelling(corner1, toBeFelled); - } - - if (!isTooAggressive(currentBlock, corner2) && BlockChecks.treeFellerCompatible(corner2) && !toBeFelled.contains(corner2)) { - processTreeFelling(corner2, toBeFelled); - } - - if (!isTooAggressive(currentBlock, corner3) && BlockChecks.treeFellerCompatible(corner3) && !toBeFelled.contains(corner3)) { - processTreeFelling(corner3, toBeFelled); - } - - if (!isTooAggressive(currentBlock, corner4) && BlockChecks.treeFellerCompatible(corner4) && !toBeFelled.contains(corner4)) { - processTreeFelling(corner4, toBeFelled); + if (toBeFelled.size() >= Config.getInstance().getTreeFellerThreshold()) { + treeFellerReachedThreshold = true; + return; + } } } } - if (BlockChecks.treeFellerCompatible(yPositive)) { - if (!mcMMO.placeStore.isTrue(currentBlock) && !toBeFelled.contains(yPositive)) { - processTreeFelling(yPositive, toBeFelled); + // Recursive call for each log found + for (Block futurCenterBlock : futureCenterBlocks) { + if (treeFellerReachedThreshold) { + return; } + + processTreeFellerAroundBlock(futurCenterBlock, toBeFelled); } } /** - * Check if Tree Feller is being too aggressive. + * Process Tree Feller. * - * @param currentBlock The current block being felled - * @param newBlock The next block to be felled - * @return true if Tree Feller is too aggressive, false otherwise + * @param event Event to process + * @return List of blocks to be removed */ - private static boolean isTooAggressive(Block currentBlock, Block newBlock) { - Material currentType = currentBlock.getType(); - Material newType = newBlock.getType(); + private static List processTreeFeller(BlockBreakEvent event) { + List toBeFelled = new ArrayList(); - if ((currentType.equals(Material.LEAVES) || currentType.equals(Material.AIR) || (Config.getInstance().getBlockModsEnabled() && ModChecks.isCustomLeafBlock(currentBlock))) && (newType.equals(Material.LEAVES) || newType.equals(Material.AIR) || (Config.getInstance().getBlockModsEnabled() && ModChecks.isCustomLeafBlock(currentBlock)))) { - return true; + processTreeFellerAroundBlock(event.getBlock(), toBeFelled); + + if (treeFellerReachedThreshold) { + treeFellerReachedThreshold = false; + + event.getPlayer().sendMessage(LocaleLoader.getString("Woodcutting.Skills.TreeFellerThreshold")); + return null; } - return false; + return toBeFelled; } /** * Check for double drops. * * @param player Player breaking the block - * @param block The block being broken + * @param block Block being broken */ private static void woodCuttingProcCheck(Player player, Block block) { - - final int MAX_CHANCE = advancedConfig.getMiningDoubleDropChance(); - final int MAX_BONUS_LEVEL = advancedConfig.getMiningDoubleDropMaxLevel(); - - int skillLevel = Users.getProfile(player).getSkillLevel(SkillType.WOODCUTTING); + final int MAX_CHANCE = ADVANCED_CONFIG.getMiningDoubleDropChance(); + final int MAX_BONUS_LEVEL = ADVANCED_CONFIG.getMiningDoubleDropMaxLevel(); byte type = block.getData(); if ((type & 0x4) == 0x4) @@ -351,17 +252,27 @@ public class WoodCutting { if ((type & 0x8) == 0x8) type ^= 0x8; +<<<<<<< Upstream, based on origin/master Material mat = Material.getMaterial(block.getTypeId()); int chance = (int) (((double) MAX_CHANCE / (double) MAX_BONUS_LEVEL) * skillLevel); if (chance > MAX_CHANCE) chance = MAX_CHANCE; +======= + Material blockMaterial = block.getType(); + int randomChance = 100; + int chance = (int) (((double) MAX_CHANCE / (double) MAX_BONUS_LEVEL) * Users.getProfile(player).getSkillLevel(SkillType.WOODCUTTING)); + + if (chance > MAX_CHANCE) { + chance = MAX_CHANCE; + } +>>>>>>> f510cb2 Optimized Tree Feller And cleaned up WoodCutting a little int activationChance = Misc.calculateActivationChance(Permissions.luckyWoodcutting(player)); if (chance > Misc.getRandom().nextInt(activationChance) && Permissions.woodcuttingDoubleDrops(player)) { Config configInstance = Config.getInstance(); - ItemStack item; - Location location; + ItemStack item = null; + Location location = null; if (configInstance.getBlockModsEnabled() && ModChecks.isCustomLogBlock(block)) { CustomBlock customBlock = ModChecks.getCustomBlock(block); @@ -380,14 +291,10 @@ public class WoodCutting { } } else { - item = (new MaterialData(mat, type)).toItemStack(1); - + item = (new MaterialData(blockMaterial, type)).toItemStack(1); location = block.getLocation(); - TreeSpecies species = TreeSpecies.getByData(type); - - /* Drop the block */ - switch (species) { + switch (TreeSpecies.getByData(type)) { case GENERIC: if (configInstance.getOakDoubleDropsEnabled()) { Misc.dropItem(location, item); @@ -422,17 +329,16 @@ public class WoodCutting { /** * Check XP gain for woodcutting. * - * @param player The player breaking the block - * @param block The block being broken + * @param player Player breaking the block + * @param block Block being broken */ public static void woodcuttingBlockCheck(Player player, Block block) { - PlayerProfile profile = Users.getProfile(player); - int xp = 0; - if (mcMMO.placeStore.isTrue(block)) { return; } + int xp = 0; + if (Config.getInstance().getBlockModsEnabled() && ModChecks.isCustomLogBlock(block)) { xp = ModChecks.getCustomBlock(block).getXpGain(); } @@ -447,8 +353,8 @@ public class WoodCutting { TreeSpecies species = TreeSpecies.getByData(type); - //Apparently species can be null in certain cases (custom server mods?) - //https://github.com/mcMMO-Dev/mcMMO/issues/229 + // Apparently species can be null in certain cases (custom server mods?) + // https://github.com/mcMMO-Dev/mcMMO/issues/229 if (species == null) return; @@ -475,7 +381,7 @@ public class WoodCutting { } WoodCutting.woodCuttingProcCheck(player, block); - Skills.xpProcessing(player, profile, SkillType.WOODCUTTING, xp); + Skills.xpProcessing(player, Users.getProfile(player), SkillType.WOODCUTTING, xp); } /** @@ -485,19 +391,25 @@ public class WoodCutting { * @param block Block being broken */ public static void leafBlower(Player player, Block block) { - FakePlayerAnimationEvent armswing = new FakePlayerAnimationEvent(player); - mcMMO.p.getServer().getPluginManager().callEvent(armswing); + mcMMO.p.getServer().getPluginManager().callEvent(new FakePlayerAnimationEvent(player)); if (mcMMO.spoutEnabled) { SpoutSounds.playSoundForPlayer(SoundEffect.POP, player, block.getLocation()); } } - private static int durabilityLossCalulate(ArrayList toBeFelled, int level) { - int durabilityLoss = 0; - for (Block x : toBeFelled) { - if (Misc.getRandom().nextInt(level + 1) > 0) {}//Don't add durabilityLoss, because Unbreaking enchantment does it's work. - else if (x.getType().equals(Material.LOG) || (Config.getInstance().getBlockModsEnabled() && ModChecks.isCustomLogBlock(x))) { + /** + * Calculate the durability loss from Tree Feller + * + * @param List Blocks to be felled + * @return Durability loss + */ + private static short calulateDurabilityLossFromTreeFeller(List toBeFelled) { + short durabilityLoss = 0; + boolean blockModsEnabled = Config.getInstance().getBlockModsEnabled(); + + for (Block block : toBeFelled) { + if (block.getType() == Material.LOG || (blockModsEnabled && ModChecks.isCustomLogBlock(block))) { durabilityLoss += Misc.toolDurabilityLoss; } } diff --git a/src/main/java/com/gmail/nossr50/util/BlockChecks.java b/src/main/java/com/gmail/nossr50/util/BlockChecks.java index 15c9f8ee0..2239d392a 100644 --- a/src/main/java/com/gmail/nossr50/util/BlockChecks.java +++ b/src/main/java/com/gmail/nossr50/util/BlockChecks.java @@ -283,7 +283,6 @@ public class BlockChecks { switch (block.getType()) { case LOG: case LEAVES: - case AIR: return true; default: