diff --git a/Changelog.txt b/Changelog.txt index e2cad02ca..bc47635e1 100644 --- a/Changelog.txt +++ b/Changelog.txt @@ -1,4 +1,13 @@ Version 2.1.156 + Added Woodcutting skill 'Knock on Wood' - This ability gives you goodies (saplings, xp orbs, apples, etc) when using Tree Feller + Tree Feller no longer gives non-wood items by default, it now requires Knock on Wood for additional loot + Added new permission node 'mcmmo.ability.woodcutting.knockonwood' + Added new locale line 'Woodcutting.SubSkill.KnockOnWood.Name' + Added new locale line 'Woodcutting.SubSkill.KnockOnWood.Stat' + Added new locale line 'Woodcutting.SubSkill.KnockOnWood.Description' + Added new locale line 'Woodcutting.SubSkill.KnockOnWood.Loot.Normal' + Added new locale line 'Woodcutting.SubSkill.KnockOnWood.Loot.Rank2' + When you raise your axe you will now see information about any super abilities on CD Fixed a bug where Green Thumb would replant blocks floating in the air Fixed a bug where the admin and party chat toggles in chat.yml didn't function as intended * Fixed a bug where Master Angler rank 1 level requirement was set too high (default configs) @@ -8,6 +17,7 @@ Version 2.1.156 Removed incorrect translations of Master Angler from various locales Modified Master Angler stat lines in /fishing Updated Green Thumb description to mention that it needs a hoe + 'Abilities.Limits.Tree_Feller_Threshold' in config.yml now defaults to 1000 instead of 500 NOTES: You don't need to touch your config files, this update handles everything automagically. diff --git a/src/main/java/com/gmail/nossr50/commands/skills/WoodcuttingCommand.java b/src/main/java/com/gmail/nossr50/commands/skills/WoodcuttingCommand.java index 505a37c8d..a817a312d 100644 --- a/src/main/java/com/gmail/nossr50/commands/skills/WoodcuttingCommand.java +++ b/src/main/java/com/gmail/nossr50/commands/skills/WoodcuttingCommand.java @@ -22,6 +22,7 @@ public class WoodcuttingCommand extends SkillCommand { private boolean canTreeFell; private boolean canLeafBlow; private boolean canDoubleDrop; + private boolean canKnockOnWood; private boolean canSplinter; private boolean canBarkSurgeon; private boolean canNaturesBounty; @@ -56,6 +57,7 @@ public class WoodcuttingCommand extends SkillCommand { canTreeFell = RankUtils.hasUnlockedSubskill(player, SubSkillType.WOODCUTTING_TREE_FELLER) && Permissions.treeFeller(player); canDoubleDrop = canUseSubskill(player, SubSkillType.WOODCUTTING_HARVEST_LUMBER) && !skill.getDoubleDropsDisabled() && RankUtils.getRank(player, SubSkillType.WOODCUTTING_HARVEST_LUMBER) >= 1; canLeafBlow = canUseSubskill(player, SubSkillType.WOODCUTTING_LEAF_BLOWER); + canKnockOnWood = canTreeFell && canUseSubskill(player, SubSkillType.WOODCUTTING_KNOCK_ON_WOOD); /*canSplinter = canUseSubskill(player, SubSkillType.WOODCUTTING_SPLINTER); canBarkSurgeon = canUseSubskill(player, SubSkillType.WOODCUTTING_BARK_SURGEON); canNaturesBounty = canUseSubskill(player, SubSkillType.WOODCUTTING_NATURES_BOUNTY);*/ @@ -69,6 +71,18 @@ public class WoodcuttingCommand extends SkillCommand { messages.add(getStatMessage(SubSkillType.WOODCUTTING_HARVEST_LUMBER, doubleDropChance) + (isLucky ? LocaleLoader.getString("Perks.Lucky.Bonus", doubleDropChanceLucky) : "")); } + + if (canKnockOnWood) { + String lootNote; + + if(RankUtils.hasReachedRank(2, player, SubSkillType.WOODCUTTING_KNOCK_ON_WOOD)) { + lootNote = LocaleLoader.getString("Woodcutting.SubSkill.KnockOnWood.Loot.Rank2"); + } else { + lootNote = LocaleLoader.getString("Woodcutting.SubSkill.KnockOnWood.Loot.Normal"); + } + + messages.add(getStatMessage(SubSkillType.WOODCUTTING_KNOCK_ON_WOOD, lootNote)); + } if (canLeafBlow) { messages.add(LocaleLoader.getString("Ability.Generic.Template", LocaleLoader.getString("Woodcutting.Ability.0"), LocaleLoader.getString("Woodcutting.Ability.1"))); diff --git a/src/main/java/com/gmail/nossr50/config/Config.java b/src/main/java/com/gmail/nossr50/config/Config.java index d3fdb5c74..d1368dc9b 100644 --- a/src/main/java/com/gmail/nossr50/config/Config.java +++ b/src/main/java/com/gmail/nossr50/config/Config.java @@ -450,7 +450,7 @@ public class Config extends AutoUpdateConfigLoader { public int getAbilityToolDamage() { return config.getInt("Abilities.Tools.Durability_Loss", 1); } /* Thresholds */ - public int getTreeFellerThreshold() { return config.getInt("Abilities.Limits.Tree_Feller_Threshold", 500); } + public int getTreeFellerThreshold() { return config.getInt("Abilities.Limits.Tree_Feller_Threshold", 1000); } /* * SKILL SETTINGS diff --git a/src/main/java/com/gmail/nossr50/datatypes/player/McMMOPlayer.java b/src/main/java/com/gmail/nossr50/datatypes/player/McMMOPlayer.java index 21459c837..5aae58596 100644 --- a/src/main/java/com/gmail/nossr50/datatypes/player/McMMOPlayer.java +++ b/src/main/java/com/gmail/nossr50/datatypes/player/McMMOPlayer.java @@ -14,8 +14,10 @@ import com.gmail.nossr50.datatypes.mods.CustomTool; import com.gmail.nossr50.datatypes.party.Party; import com.gmail.nossr50.datatypes.party.PartyTeleportRecord; import com.gmail.nossr50.datatypes.skills.PrimarySkillType; +import com.gmail.nossr50.datatypes.skills.SubSkillType; import com.gmail.nossr50.datatypes.skills.SuperAbilityType; import com.gmail.nossr50.datatypes.skills.ToolType; +import com.gmail.nossr50.datatypes.skills.subskills.interfaces.SubSkill; import com.gmail.nossr50.locale.LocaleLoader; import com.gmail.nossr50.mcMMO; import com.gmail.nossr50.party.PartyManager; @@ -40,6 +42,7 @@ import com.gmail.nossr50.skills.swords.SwordsManager; import com.gmail.nossr50.skills.taming.TamingManager; import com.gmail.nossr50.skills.unarmed.UnarmedManager; import com.gmail.nossr50.skills.woodcutting.WoodcuttingManager; +import com.gmail.nossr50.util.BlockUtils; import com.gmail.nossr50.util.EventUtils; import com.gmail.nossr50.util.Misc; import com.gmail.nossr50.util.Permissions; @@ -57,6 +60,7 @@ import net.kyori.adventure.identity.Identity; import org.apache.commons.lang.Validate; import org.bukkit.GameMode; import org.bukkit.Location; +import org.bukkit.block.Block; import org.bukkit.entity.Player; import org.bukkit.inventory.ItemStack; import org.bukkit.metadata.FixedMetadataValue; @@ -918,14 +922,25 @@ public class McMMOPlayer implements Identified { if (skill != PrimarySkillType.WOODCUTTING && skill != PrimarySkillType.AXES) { int timeRemaining = calculateTimeRemaining(ability); - if (!getAbilityMode(ability) && timeRemaining > 0) { + if (isAbilityOnCooldown(ability)) { NotificationManager.sendPlayerInformation(player, NotificationType.ABILITY_COOLDOWN, "Skills.TooTired", String.valueOf(timeRemaining)); return; } } if (Config.getInstance().getAbilityMessagesEnabled()) { - NotificationManager.sendPlayerInformation(player, NotificationType.TOOL, tool.getRaiseTool()); + /* + * + * IF THE TOOL IS AN AXE + * + */ + if(tool == ToolType.AXE) { + processAxeToolMessages(); + } else { + NotificationManager.sendPlayerInformation(player, NotificationType.TOOL, tool.getRaiseTool()); + } + + //Send Sound SoundManager.sendSound(player, player.getLocation(), SoundType.TOOL_READY); } @@ -934,6 +949,53 @@ public class McMMOPlayer implements Identified { } } + public void processAxeToolMessages() { + Block rayCast = player.getTargetBlock(null, 100); + + /* + * IF BOTH TREE FELLER & SKULL SPLITTER ARE ON CD + */ + if(isAbilityOnCooldown(SuperAbilityType.TREE_FELLER) && isAbilityOnCooldown(SuperAbilityType.SKULL_SPLITTER)) { + tooTiredMultiple(PrimarySkillType.WOODCUTTING, SubSkillType.WOODCUTTING_TREE_FELLER, SuperAbilityType.TREE_FELLER, SubSkillType.AXES_SKULL_SPLITTER, SuperAbilityType.SKULL_SPLITTER); + /* + * IF TREE FELLER IS ON CD + * AND PLAYER IS LOOKING AT TREE + */ + } else if(isAbilityOnCooldown(SuperAbilityType.TREE_FELLER) + && BlockUtils.isPartOfTree(rayCast)) { + raiseToolWithCooldowns(SubSkillType.WOODCUTTING_TREE_FELLER, SuperAbilityType.TREE_FELLER); + + /* + * IF SKULL SPLITTER IS ON CD + */ + } else if(isAbilityOnCooldown(SuperAbilityType.SKULL_SPLITTER)) { + raiseToolWithCooldowns(SubSkillType.AXES_SKULL_SPLITTER, SuperAbilityType.SKULL_SPLITTER); + } else { + NotificationManager.sendPlayerInformation(player, NotificationType.TOOL, ToolType.AXE.getRaiseTool()); + } + } + + private void tooTiredMultiple(PrimarySkillType primarySkillType, SubSkillType aSubSkill, SuperAbilityType aSuperAbility, SubSkillType bSubSkill, SuperAbilityType bSuperAbility) { + String aSuperAbilityCD = LocaleLoader.getString("Skills.TooTired.Named", aSubSkill.getLocaleName(), String.valueOf(calculateTimeRemaining(aSuperAbility))); + String bSuperAbilityCD = LocaleLoader.getString("Skills.TooTired.Named", bSubSkill.getLocaleName(), String.valueOf(calculateTimeRemaining(bSuperAbility))); + String allCDStr = aSuperAbilityCD + ", " + bSuperAbilityCD; + + NotificationManager.sendPlayerInformation(player, NotificationType.TOOL, "Skills.TooTired.Extra", + primarySkillType.getName(), + allCDStr); + } + + private void raiseToolWithCooldowns(SubSkillType subSkillType, SuperAbilityType superAbilityType) { + NotificationManager.sendPlayerInformation(player, NotificationType.TOOL, + "Axes.Ability.Ready.Extra", + subSkillType.getLocaleName(), + String.valueOf(calculateTimeRemaining(superAbilityType))); + } + + public boolean isAbilityOnCooldown(SuperAbilityType ability) { + return !getAbilityMode(ability) && calculateTimeRemaining(ability) > 0; + } + /** * Calculate the time remaining until the ability's cooldown expires. * diff --git a/src/main/java/com/gmail/nossr50/datatypes/skills/PrimarySkillType.java b/src/main/java/com/gmail/nossr50/datatypes/skills/PrimarySkillType.java index 8f022ab6c..6acc8cf5d 100644 --- a/src/main/java/com/gmail/nossr50/datatypes/skills/PrimarySkillType.java +++ b/src/main/java/com/gmail/nossr50/datatypes/skills/PrimarySkillType.java @@ -63,7 +63,7 @@ public enum PrimarySkillType { UNARMED(UnarmedManager.class, Color.BLACK, SuperAbilityType.BERSERK, ToolType.FISTS, ImmutableList.of(SubSkillType.UNARMED_BERSERK, SubSkillType.UNARMED_UNARMED_LIMIT_BREAK, SubSkillType.UNARMED_BLOCK_CRACKER, SubSkillType.UNARMED_ARROW_DEFLECT, SubSkillType.UNARMED_DISARM, SubSkillType.UNARMED_STEEL_ARM_STYLE, SubSkillType.UNARMED_IRON_GRIP)), WOODCUTTING(WoodcuttingManager.class, Color.OLIVE, SuperAbilityType.TREE_FELLER, ToolType.AXE, - ImmutableList.of(SubSkillType.WOODCUTTING_LEAF_BLOWER, SubSkillType.WOODCUTTING_TREE_FELLER, SubSkillType.WOODCUTTING_HARVEST_LUMBER)); + ImmutableList.of(SubSkillType.WOODCUTTING_LEAF_BLOWER, SubSkillType.WOODCUTTING_TREE_FELLER, SubSkillType.WOODCUTTING_HARVEST_LUMBER, SubSkillType.WOODCUTTING_KNOCK_ON_WOOD)); private final Class managerClass; private final Color skillColor; diff --git a/src/main/java/com/gmail/nossr50/datatypes/skills/SubSkillType.java b/src/main/java/com/gmail/nossr50/datatypes/skills/SubSkillType.java index 4d36fe7fb..059df8db9 100644 --- a/src/main/java/com/gmail/nossr50/datatypes/skills/SubSkillType.java +++ b/src/main/java/com/gmail/nossr50/datatypes/skills/SubSkillType.java @@ -101,6 +101,7 @@ public enum SubSkillType { /* Woodcutting */ /* WOODCUTTING_BARK_SURGEON(3),*/ + WOODCUTTING_KNOCK_ON_WOOD(2), WOODCUTTING_HARVEST_LUMBER(1), WOODCUTTING_LEAF_BLOWER(1), /* WOODCUTTING_NATURES_BOUNTY(3), diff --git a/src/main/java/com/gmail/nossr50/skills/excavation/ExcavationManager.java b/src/main/java/com/gmail/nossr50/skills/excavation/ExcavationManager.java index b12805856..14eda7e32 100644 --- a/src/main/java/com/gmail/nossr50/skills/excavation/ExcavationManager.java +++ b/src/main/java/com/gmail/nossr50/skills/excavation/ExcavationManager.java @@ -15,8 +15,6 @@ import com.gmail.nossr50.util.skills.RankUtils; import com.gmail.nossr50.util.skills.SkillUtils; import org.bukkit.Location; import org.bukkit.block.BlockState; -import org.bukkit.entity.EntityType; -import org.bukkit.entity.ExperienceOrb; import org.bukkit.entity.Player; import java.util.List; @@ -47,8 +45,7 @@ public class ExcavationManager extends SkillManager { //Spawn Vanilla XP orbs if a dice roll succeeds if(RandomChanceUtil.rollDice(getArchaelogyExperienceOrbChance(), 100)) { - ExperienceOrb experienceOrb = (ExperienceOrb) getPlayer().getWorld().spawnEntity(location, EntityType.EXPERIENCE_ORB); - experienceOrb.setExperience(getExperienceOrbsReward()); + Misc.spawnExperienceOrb(location, getExperienceOrbsReward()); } xp += treasure.getXp(); diff --git a/src/main/java/com/gmail/nossr50/skills/woodcutting/WoodcuttingManager.java b/src/main/java/com/gmail/nossr50/skills/woodcutting/WoodcuttingManager.java index 46d466839..9dd45742a 100644 --- a/src/main/java/com/gmail/nossr50/skills/woodcutting/WoodcuttingManager.java +++ b/src/main/java/com/gmail/nossr50/skills/woodcutting/WoodcuttingManager.java @@ -301,8 +301,20 @@ public class WoodcuttingManager extends SkillManager { processHarvestLumber(blockState); } else if (BlockUtils.isNonWoodPartOfTree(blockState)) { //Drop displaced non-woodcutting XP blocks -// Misc.spawnItemsFromCollection(Misc.getBlockCenter(blockState), block.getDrops(), ItemSpawnReason.TREE_FELLER_DISPLACED_BLOCK, 1); - Misc.spawnItemsFromCollection(Misc.getBlockCenter(blockState), block.getDrops(), ItemSpawnReason.TREE_FELLER_DISPLACED_BLOCK); + + if(RankUtils.hasUnlockedSubskill(player, SubSkillType.WOODCUTTING_KNOCK_ON_WOOD)) { + Misc.spawnItemsFromCollection(Misc.getBlockCenter(blockState), block.getDrops(), ItemSpawnReason.TREE_FELLER_DISPLACED_BLOCK); + + if(RankUtils.hasReachedRank(2, player, SubSkillType.WOODCUTTING_KNOCK_ON_WOOD)) { + if(RandomChanceUtil.rollDice(75, 100)) { + int randOrbCount = Math.max(1, Misc.getRandom().nextInt(50)); + Misc.spawnExperienceOrb(blockState.getLocation(), randOrbCount); + } + } + + } else { + Misc.spawnItemsFromCollection(Misc.getBlockCenter(blockState), block.getDrops(), ItemSpawnReason.TREE_FELLER_DISPLACED_BLOCK, 1); + } } blockState.setType(Material.AIR); diff --git a/src/main/java/com/gmail/nossr50/util/BlockUtils.java b/src/main/java/com/gmail/nossr50/util/BlockUtils.java index 17a2d82f2..67da9f4af 100644 --- a/src/main/java/com/gmail/nossr50/util/BlockUtils.java +++ b/src/main/java/com/gmail/nossr50/util/BlockUtils.java @@ -11,6 +11,7 @@ import com.gmail.nossr50.skills.salvage.Salvage; import com.gmail.nossr50.util.random.RandomChanceSkill; import com.gmail.nossr50.util.random.RandomChanceUtil; import org.bukkit.Material; +import org.bukkit.block.Block; import org.bukkit.block.BlockState; import org.bukkit.block.data.Ageable; import org.bukkit.block.data.BlockData; @@ -179,6 +180,10 @@ public final class BlockUtils { return mcMMO.getMaterialMapStore().isTreeFellerDestructible(blockState.getType()); } + public static boolean isNonWoodPartOfTree(Material material) { + return mcMMO.getMaterialMapStore().isTreeFellerDestructible(material); + } + /** * Determine if a given block should be affected by Flux Mining * @@ -274,4 +279,8 @@ public final class BlockUtils { } return true; } + + public static boolean isPartOfTree(Block rayCast) { + return hasWoodcuttingXP(rayCast.getState()) || isNonWoodPartOfTree(rayCast.getType()); + } } diff --git a/src/main/java/com/gmail/nossr50/util/Misc.java b/src/main/java/com/gmail/nossr50/util/Misc.java index 41195c08d..9b74f110c 100644 --- a/src/main/java/com/gmail/nossr50/util/Misc.java +++ b/src/main/java/com/gmail/nossr50/util/Misc.java @@ -12,6 +12,7 @@ import org.bukkit.Material; import org.bukkit.block.BlockState; import org.bukkit.entity.*; import org.bukkit.inventory.ItemStack; +import org.bukkit.scheduler.BukkitRunnable; import org.bukkit.util.Vector; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; @@ -310,4 +311,37 @@ public final class Misc { public static boolean isPartyLeader(@NotNull McMMOPlayer mmoPlayer) { return mmoPlayer.getParty().getLeader().getUniqueId().equals(mmoPlayer.getPlayer().getUniqueId()); } + +// public static void spawnExperienceOrb(@NotNull Location location, int orbAmount, int experienceValue) { +// for (int i = 0; i < orbAmount; i++) { +// new SpawnOrbTask(location, experienceValue).runTaskLater(mcMMO.p, 20); +// } +// } + + public static void spawnExperienceOrb(@NotNull Location location, int experienceValue) { + if(location.getWorld() == null) + return; + + ExperienceOrb experienceOrb = (ExperienceOrb) location.getWorld().spawnEntity(location, EntityType.EXPERIENCE_ORB); + experienceOrb.setExperience(experienceValue); + } + + private static class SpawnOrbTask extends BukkitRunnable { + private final Location location; + private int orbExpValue; + + private SpawnOrbTask(Location location, int orbExpValue) { + this.location = location; + this.orbExpValue = orbExpValue; + } + + @Override + public void run() { + if(location == null || location.getWorld() == null) + return; + + ExperienceOrb experienceOrb = (ExperienceOrb) location.getWorld().spawnEntity(location, EntityType.EXPERIENCE_ORB); + experienceOrb.setExperience(orbExpValue); + } + } } diff --git a/src/main/resources/config.yml b/src/main/resources/config.yml index b9e299eeb..748d8a6ba 100644 --- a/src/main/resources/config.yml +++ b/src/main/resources/config.yml @@ -303,7 +303,7 @@ Abilities: Super_Breaker: 0 Tree_Feller: 0 Limits: - Tree_Feller_Threshold: 500 + Tree_Feller_Threshold: 1000 Tools: # Use more tool durability while using abilities. Set Durability_Loss to 0 to disable the extra durability damage. Durability_Loss: 1 diff --git a/src/main/resources/locale/locale_en_US.properties b/src/main/resources/locale/locale_en_US.properties index f2ce907e6..585d9a5f9 100644 --- a/src/main/resources/locale/locale_en_US.properties +++ b/src/main/resources/locale/locale_en_US.properties @@ -184,6 +184,7 @@ Axes.Ability.Bonus.4=Greater Impact Axes.Ability.Bonus.5=Deal {0} Bonus DMG to unarmored foes Axes.Ability.Lower=&7You lower your Axe. Axes.Ability.Ready=&3You &6ready&3 your Axe. +Axes.Ability.Ready.Extra=&3You &6ready&3 your Axe. [[GRAY]]({0} is on cooldown for {1}s) Axes.Combat.CritStruck=&4You were CRITICALLY hit! Axes.Combat.CriticalHit=CRITICAL HIT! Axes.Combat.GI.Proc=&a**STRUCK WITH GREAT FORCE** @@ -531,6 +532,11 @@ Woodcutting.SubSkill.TreeFeller.Description=Make trees explode Woodcutting.SubSkill.TreeFeller.Stat=Tree Feller Length Woodcutting.SubSkill.LeafBlower.Name=Leaf Blower Woodcutting.SubSkill.LeafBlower.Description=Blow Away Leaves +Woodcutting.SubSkill.KnockOnWood.Name=Knock On Wood +Woodcutting.SubSkill.KnockOnWood.Description=Find additional goodies when using Tree Feller +Woodcutting.SubSkill.KnockOnWood.Stat=Knock on Wood +Woodcutting.SubSkill.KnockOnWood.Loot.Normal=Standard loot from trees +Woodcutting.SubSkill.KnockOnWood.Loot.Rank2=Standard loot from trees and experience orbs Woodcutting.SubSkill.HarvestLumber.Name=Harvest Lumber Woodcutting.SubSkill.HarvestLumber.Description=Skillfully extract more Lumber Woodcutting.SubSkill.HarvestLumber.Stat=Double Drop Chance @@ -989,6 +995,8 @@ Skills.Stats={0}&a{1}&3 XP(&7{2}&3/&7{3}&3) Skills.ChildStats={0}&a{1} Skills.MaxXP=Max Skills.TooTired=You are too tired to use that ability again. &e({0}s) +Skills.TooTired.Named=[[GRAY]](&6{0}&e {1}s[[GRAY]]) +Skills.TooTired.Extra=&6{0} &eSuper Ability CDs - {1} Skills.Cancelled=&6{0} &ccancelled! Skills.ConfirmOrCancel=&aRight-click again to confirm &6{0}&a. Left-click to cancel. Skills.AbilityGateRequirementFail=&7You require &e{0}&7 more levels of &3{1}&7 to use this super ability. diff --git a/src/main/resources/plugin.yml b/src/main/resources/plugin.yml index e3ec1e2b5..9bbf76aa9 100644 --- a/src/main/resources/plugin.yml +++ b/src/main/resources/plugin.yml @@ -695,8 +695,11 @@ permissions: mcmmo.ability.woodcutting.splinter: true mcmmo.ability.woodcutting.barksurgeon: true mcmmo.ability.woodcutting.naturesbounty: true + mcmmo.ability.woodcutting.knockonwood: true mcmmo.ability.woodcutting.leafblower: true mcmmo.ability.woodcutting.treefeller: true + mcmmo.ability.woodcutting.knockonwood: + description: Allows access to Knock on Wood subskill mcmmo.ability.woodcutting.splinter: description: Allows access to Splinter mcmmo.ability.woodcutting.barksurgeon: diff --git a/src/main/resources/skillranks.yml b/src/main/resources/skillranks.yml index 3c5cac24b..6e68929a0 100644 --- a/src/main/resources/skillranks.yml +++ b/src/main/resources/skillranks.yml @@ -605,15 +605,6 @@ Unarmed: Rank_20: 1000 Woodcutting: - Splinter: - Standard: - Rank_1: 5 - Rank_2: 30 - Rank_3: 55 - RetroMode: - Rank_1: 50 - Rank_2: 300 - Rank_3: 550 TreeFeller: Standard: Rank_1: 5 @@ -627,29 +618,18 @@ Woodcutting: Rank_3: 500 Rank_4: 750 Rank_5: 1000 - BarkSurgeon: - Standard: - Rank_1: 70 - Rank_2: 80 - Rank_3: 95 - RetroMode: - Rank_1: 700 - Rank_2: 800 - Rank_3: 950 - NaturesBounty: - Standard: - Rank_1: 40 - Rank_2: 60 - Rank_3: 90 - RetroMode: - Rank_1: 400 - Rank_2: 600 - Rank_3: 900 HarvestLumber: Standard: Rank_1: 1 RetroMode: Rank_1: 1 + KnockOnWood: + Standard: + Rank_1: 30 + Rank_2: 60 + RetroMode: + Rank_1: 300 + Rank_2: 600 LeafBlower: Standard: Rank_1: 15