diff --git a/src/main/java/com/gmail/nossr50/commands/skills/RepairCommand.java b/src/main/java/com/gmail/nossr50/commands/skills/RepairCommand.java index 5d538a9f1..30ebf33e5 100644 --- a/src/main/java/com/gmail/nossr50/commands/skills/RepairCommand.java +++ b/src/main/java/com/gmail/nossr50/commands/skills/RepairCommand.java @@ -26,7 +26,6 @@ public class RepairCommand extends SkillCommand { private boolean canSuperRepair; private boolean canMasterRepair; private boolean canArcaneForge; - private boolean canSalvage; private boolean canRepairStone; private boolean canRepairIron; private boolean canRepairGold; @@ -77,7 +76,6 @@ public class RepairCommand extends SkillCommand { canSuperRepair = Permissions.secondaryAbilityEnabled(player, SecondaryAbility.SUPER_REPAIR); canMasterRepair = Permissions.secondaryAbilityEnabled(player, SecondaryAbility.REPAIR_MASTERY); canArcaneForge = Permissions.secondaryAbilityEnabled(player, SecondaryAbility.ARCANE_FORGING); - canSalvage = Permissions.secondaryAbilityEnabled(player, SecondaryAbility.SALVAGE); canRepairDiamond = Permissions.repairDiamond(player); canRepairGold = Permissions.repairGold(player); canRepairIron = Permissions.repairIron(player); @@ -122,10 +120,6 @@ public class RepairCommand extends SkillCommand { messages.add(LocaleLoader.getString("Effects.Template", LocaleLoader.getString("Repair.Effect.6", diamondLevel), LocaleLoader.getString("Repair.Effect.7"))); } - if (canSalvage && Repair.salvageUnlockLevel > 0) { - messages.add(LocaleLoader.getString("Effects.Template", LocaleLoader.getString("Repair.Effect.16", Repair.salvageUnlockLevel), LocaleLoader.getString("Repair.Effect.17"))); - } - if (canArcaneForge) { messages.add(LocaleLoader.getString("Effects.Template", LocaleLoader.getString("Repair.Effect.8"), LocaleLoader.getString("Repair.Effect.9"))); } diff --git a/src/main/java/com/gmail/nossr50/commands/skills/SalvageCommand.java b/src/main/java/com/gmail/nossr50/commands/skills/SalvageCommand.java new file mode 100644 index 000000000..bbffcddc2 --- /dev/null +++ b/src/main/java/com/gmail/nossr50/commands/skills/SalvageCommand.java @@ -0,0 +1,79 @@ +package com.gmail.nossr50.commands.skills; + +import java.util.ArrayList; +import java.util.List; + +import org.bukkit.entity.Player; + +import com.gmail.nossr50.datatypes.skills.SecondaryAbility; +import com.gmail.nossr50.datatypes.skills.SkillType; +import com.gmail.nossr50.locale.LocaleLoader; +import com.gmail.nossr50.skills.salvage.Salvage; +import com.gmail.nossr50.skills.salvage.SalvageManager; +import com.gmail.nossr50.util.Permissions; +import com.gmail.nossr50.util.player.UserManager; + +public class SalvageCommand extends SkillCommand { + private boolean canAdvancedSalvage; + private boolean canArcaneSalvage; + + public SalvageCommand() { + super(SkillType.SALVAGE); + } + + @Override + protected void dataCalculations(Player player, float skillValue, boolean isLucky) { + // TODO Auto-generated method stub + + } + + @Override + protected void permissionsCheck(Player player) { + canAdvancedSalvage = Permissions.secondaryAbilityEnabled(player, SecondaryAbility.ADVANCED_SALVAGE); + canArcaneSalvage = Permissions.secondaryAbilityEnabled(player, SecondaryAbility.ARCANE_SALVAGE); + } + + @Override + protected List effectsDisplay() { + List messages = new ArrayList(); + + if (canAdvancedSalvage) { + messages.add(LocaleLoader.getString("Effects.Template", LocaleLoader.getString("Salvage.Effect.0"), LocaleLoader.getString("Salvage.Effect.1"))); + } + + if (canArcaneSalvage) { + messages.add(LocaleLoader.getString("Effects.Template", LocaleLoader.getString("Salvage.Effect.2"), LocaleLoader.getString("Salvage.Effect.3"))); + } + + return messages; + } + + @Override + protected List statsDisplay(Player player, float skillValue, boolean hasEndurance, boolean isLucky) { + List messages = new ArrayList(); + if (canAdvancedSalvage) { + if (skillValue < Salvage.advancedSalvageUnlockLevel) { + messages.add(LocaleLoader.getString("Ability.Generic.Template.Lock", LocaleLoader.getString("Salvage.Ability.Locked.0", Salvage.advancedSalvageUnlockLevel))); + } + else { + messages.add(LocaleLoader.getString("Ability.Generic.Template", LocaleLoader.getString("Salvage.Ability.Bonus.0"), LocaleLoader.getString("Salvage.Ability.Bonus.1"))); + } + } + + if (canArcaneSalvage) { + SalvageManager salvageManager = UserManager.getPlayer(player).getSalvageManager(); + + messages.add(LocaleLoader.getString("Salvage.Arcane.Rank", salvageManager.getArcaneSalvageRank(), Salvage.Tier.EIGHT.toNumerical())); + + if (Salvage.arcaneSalvageEnchantLoss) { + messages.add(LocaleLoader.getString("Ability.Generic.Template", LocaleLoader.getString("Salvage.Arcane.ExtractFull"), salvageManager.getExtractFullEnchantChance())); + } + + if (Salvage.arcaneSalvageDowngrades) { + messages.add(LocaleLoader.getString("Ability.Generic.Template", LocaleLoader.getString("Salvage.Arcane.ExtractPartial"), salvageManager.getExtractPartialEnchantChance())); + } + } + + return messages; + } +} diff --git a/src/main/java/com/gmail/nossr50/config/AdvancedConfig.java b/src/main/java/com/gmail/nossr50/config/AdvancedConfig.java index 39b44277b..c4c57e046 100644 --- a/src/main/java/com/gmail/nossr50/config/AdvancedConfig.java +++ b/src/main/java/com/gmail/nossr50/config/AdvancedConfig.java @@ -10,6 +10,7 @@ import com.gmail.nossr50.skills.alchemy.Alchemy; import com.gmail.nossr50.skills.fishing.Fishing; import com.gmail.nossr50.skills.mining.BlastMining; import com.gmail.nossr50.skills.repair.ArcaneForging; +import com.gmail.nossr50.skills.salvage.Salvage; import com.gmail.nossr50.skills.smelting.Smelting; import com.gmail.nossr50.util.StringUtils; @@ -378,10 +379,6 @@ public class AdvancedConfig extends AutoUpdateConfigLoader { reason.add("Skills.Repair.SuperRepair.MaxBonusLevel should be at least 1!"); } - if (getSalvageUnlockLevel() < 0) { - reason.add("Skills.Repair.Salvage.UnlockLevel should be at least 0!"); - } - List arcaneForgingTierList = Arrays.asList(ArcaneForging.Tier.values()); for (ArcaneForging.Tier tier : arcaneForgingTierList) { @@ -414,6 +411,51 @@ public class AdvancedConfig extends AutoUpdateConfigLoader { } } + /* SALVAGE */ + if (getSalvageMaxPercentage() < 1) { + reason.add("Skills.Salvage.MaxPercentage should be at least 1!"); + } + + if (getSalvageMaxPercentageLevel() < 1) { + reason.add("Skills.Salvage.MaxPercentageLevel should be at least 1!"); + } + + if (getAdvancedSalvageUnlockLevel() < 1) { + reason.add("Skills.Salvage.AdvancedSalvage.UnlockLevel should be at least 1!"); + } + + List salvageTierList = Arrays.asList(Salvage.Tier.values()); + + for (Salvage.Tier tier : salvageTierList) { + if (getArcaneSalvageRankLevel(tier) < 0) { + reason.add("Skills.Salvage.ArcaneSalvage.Rank_Levels.Rank_" + tier.toNumerical() + " should be at least 0!"); + } + + if (getArcaneSalvageExtractFullEnchantsChance(tier) < 0 || getArcaneSalvageExtractFullEnchantsChance(tier) > 100) { + reason.add("Skills.Salvage.ArcaneSalvage.ExtractFullEnchant.Rank_" + tier.toNumerical() + " only accepts values from 0 to 100!"); + } + + if (getArcaneSalvageExtractPartialEnchantsChance(tier) < 0 || getArcaneSalvageExtractPartialEnchantsChance(tier) > 100) { + reason.add("Skills.Salvage.ArcaneSalvage.ExtractPartialEnchant.Rank_" + tier.toNumerical() + " only accepts values from 0 to 100!"); + } + + if (tier != Salvage.Tier.EIGHT) { + Salvage.Tier nextTier = salvageTierList.get(salvageTierList.indexOf(tier) - 1); + + if (getArcaneSalvageRankLevel(tier) >= getArcaneSalvageRankLevel(nextTier)) { + reason.add("Skills.Salvage.ArcaneSalvage.Rank_Levels.Rank_" + tier.toNumerical() + " should be less than Skills.Salvage.ArcaneSalvage.Rank_Levels.Rank_" + nextTier.toNumerical() + "!"); + } + + if (getArcaneSalvageExtractFullEnchantsChance(tier) > getArcaneSalvageExtractFullEnchantsChance(nextTier)) { + reason.add("Skills.Salvage.ArcaneSalvage.ExtractFullEnchant.Rank_" + tier.toNumerical() + " should be less than or equal to Skills.Salvage.ArcaneSalvage.ExtractFullEnchant.Rank_" + nextTier.toNumerical() + "!"); + } + + if (getArcaneSalvageExtractPartialEnchantsChance(tier) > getArcaneSalvageExtractPartialEnchantsChance(nextTier)) { + reason.add("Skills.Salvage.ArcaneSalvage.ExtractPartialEnchant.Rank_" + tier.toNumerical() + " should be less than or equal to Skills.Salvage.ArcaneSalvage.ExtractPartialEnchant.Rank_" + nextTier.toNumerical() + "!"); + } + } + } + /* SMELTING */ if (getBurnModifierMaxLevel() < 1) { reason.add("Skills.Smelting.FuelEfficiency.MaxBonusLevel should be at least 1!"); @@ -725,7 +767,8 @@ public class AdvancedConfig extends AutoUpdateConfigLoader { /* REPAIR */ public double getRepairMasteryMaxBonus() { return config.getDouble("Skills.Repair.RepairMastery.MaxBonusPercentage", 200.0D); } public int getRepairMasteryMaxLevel() { return config.getInt("Skills.Repair.RepairMastery.MaxBonusLevel", 1000); } - public int getSalvageUnlockLevel() { return config.getInt("Skills.Repair.Salvage.UnlockLevel", 600); } + public double getSuperRepairChanceMax() { return config.getDouble("Skills.Repair.SuperRepair.ChanceMax", 100.0D); } + public int getSuperRepairMaxLevel() { return config.getInt("Skills.Repair.SuperRepair.MaxBonusLevel", 1000); } /* Arcane Forging */ public int getArcaneForgingRankLevel(ArcaneForging.Tier tier) { return config.getInt("Skills.Repair.ArcaneForging.Rank_Levels.Rank_" + tier.toNumerical()); } @@ -736,6 +779,19 @@ public class AdvancedConfig extends AutoUpdateConfigLoader { public boolean getArcaneForgingDowngradeEnabled() { return config.getBoolean("Skills.Repair.ArcaneForging.Downgrades_Enabled", true); } public double getArcaneForgingDowngradeChance(ArcaneForging.Tier tier) { return config.getDouble("Skills.Repair.ArcaneForging.Downgrades_Chance.Rank_" + tier.toNumerical()); } + /* SALVAGE */ + public double getSalvageMaxPercentage() { return config.getDouble("Skills.Salvage.MaxPercentage", 100.0D); } + public int getSalvageMaxPercentageLevel() { return config.getInt("Skills.Salvage.MaxPercentageLevel", 1000); } + + public int getAdvancedSalvageUnlockLevel() { return config.getInt("Skills.Salvage.AdvancedSalvage.UnlockLevel", 350); } + + public boolean getArcaneSalvageEnchantDowngradeEnabled() { return config.getBoolean("Skills.Salvage.ArcaneSalvage.EnchantDowngradeEnabled", true); } + public boolean getArcaneSalvageEnchantLossEnabled() { return config.getBoolean("Skills.Salvage.ArcaneSalvage.EnchantLossEnabled", true); } + + public int getArcaneSalvageRankLevel(Salvage.Tier tier) { return config.getInt("Skills.Salvage.ArcaneSalvage.Rank_Levels.Rank_" + tier.toNumerical()); } + public int getArcaneSalvageExtractFullEnchantsChance(Salvage.Tier tier) { return config.getInt("Skills.Salvage.ArcaneSalvage.ExtractFullEnchant.Rank_" + tier.toNumerical()); } + public int getArcaneSalvageExtractPartialEnchantsChance(Salvage.Tier tier) { return config.getInt("Skills.Salvage.ArcaneSalvage.ExtractPartialEnchant.Rank_" + tier.toNumerical()); } + /* SMELTING */ public int getBurnModifierMaxLevel() { return config.getInt("Skills.Smelting.FuelEfficiency.MaxBonusLevel", 1000); } public double getBurnTimeMultiplier() { return config.getDouble("Skills.Smelting.FuelEfficiency.Multiplier", 3.0D); } diff --git a/src/main/java/com/gmail/nossr50/config/Config.java b/src/main/java/com/gmail/nossr50/config/Config.java index 5db353d86..3cfb4f6dc 100644 --- a/src/main/java/com/gmail/nossr50/config/Config.java +++ b/src/main/java/com/gmail/nossr50/config/Config.java @@ -464,11 +464,14 @@ public class Config extends AutoUpdateConfigLoader { public boolean getRepairAnvilPlaceSoundsEnabled() { return config.getBoolean("Skills.Repair.Anvil_Placed_Sounds", true); } public boolean getRepairAnvilUseSoundsEnabled() { return config.getBoolean("Skills.Repair.Anvil_Use_Sounds", true); } public Material getRepairAnvilMaterial() { return Material.matchMaterial(config.getString("Skills.Repair.Anvil_Material", "IRON_BLOCK")); } - public Material getSalvageAnvilMaterial() { return Material.matchMaterial(config.getString("Skills.Repair.Salvage_Anvil_Material", "GOLD_BLOCK")); } - public boolean getSalvageTools() { return config.getBoolean("Skills.Repair.Salvage_tools", true); } - public boolean getSalvageArmor() { return config.getBoolean("Skills.Repair.Salvage_armor", true); } public boolean getRepairConfirmRequired() { return config.getBoolean("Skills.Repair.Confirm_Required", true); } + /* Salvage */ + public boolean getSalvageAnvilMessagesEnabled() { return config.getBoolean("Skills.Salvage.Anvil_Messages", true); } + public Material getSalvageAnvilMaterial() { return Material.matchMaterial(config.getString("Skills.Repair.Salvage_Anvil_ID", "GOLD_BLOCK")); } + public boolean getSalvageTools() { return config.getBoolean("Skills.Salvage.Salvage_tools", true); } + public boolean getSalvageArmor() { return config.getBoolean("Skills.Salvage.Salvage_armor", true); } + /* Unarmed */ public boolean getUnarmedBlockCrackerSmoothbrickToCracked() { return config.getBoolean("Skills.Unarmed.Block_Cracker.SmoothBrick_To_CrackedBrick", true); } diff --git a/src/main/java/com/gmail/nossr50/config/mods/CustomArmorConfig.java b/src/main/java/com/gmail/nossr50/config/mods/CustomArmorConfig.java index ff2afd141..410f74f4f 100644 --- a/src/main/java/com/gmail/nossr50/config/mods/CustomArmorConfig.java +++ b/src/main/java/com/gmail/nossr50/config/mods/CustomArmorConfig.java @@ -9,11 +9,11 @@ import org.bukkit.configuration.ConfigurationSection; import org.bukkit.inventory.ItemStack; import com.gmail.nossr50.config.ConfigLoader; -import com.gmail.nossr50.skills.repair.Repair; import com.gmail.nossr50.skills.repair.repairables.RepairItemType; import com.gmail.nossr50.skills.repair.repairables.RepairMaterialType; import com.gmail.nossr50.skills.repair.repairables.Repairable; import com.gmail.nossr50.skills.repair.repairables.RepairableFactory; +import com.gmail.nossr50.util.skills.SkillUtils; public class CustomArmorConfig extends ConfigLoader { private boolean needsUpdate = false; @@ -79,7 +79,7 @@ public class CustomArmorConfig extends ConfigLoader { if (repairable) { byte repairData = (byte) config.getInt(armorType + "." + armorName + ".Repair_Material_Data_Value", -1); - int repairQuantity = Repair.getRepairAndSalvageQuantities(new ItemStack(armorMaterial), repairMaterial, repairData); + int repairQuantity = SkillUtils.getRepairAndSalvageQuantities(new ItemStack(armorMaterial), repairMaterial, repairData); if (repairQuantity == 0) { repairQuantity = config.getInt(armorType + "." + armorName + ".Repair_Material_Data_Quantity", 2); diff --git a/src/main/java/com/gmail/nossr50/config/mods/CustomToolConfig.java b/src/main/java/com/gmail/nossr50/config/mods/CustomToolConfig.java index b1fa7c8fd..5b9aa6de4 100644 --- a/src/main/java/com/gmail/nossr50/config/mods/CustomToolConfig.java +++ b/src/main/java/com/gmail/nossr50/config/mods/CustomToolConfig.java @@ -11,11 +11,11 @@ import org.bukkit.inventory.ItemStack; import com.gmail.nossr50.config.ConfigLoader; import com.gmail.nossr50.datatypes.mods.CustomTool; -import com.gmail.nossr50.skills.repair.Repair; import com.gmail.nossr50.skills.repair.repairables.RepairItemType; import com.gmail.nossr50.skills.repair.repairables.RepairMaterialType; import com.gmail.nossr50.skills.repair.repairables.Repairable; import com.gmail.nossr50.skills.repair.repairables.RepairableFactory; +import com.gmail.nossr50.util.skills.SkillUtils; public class CustomToolConfig extends ConfigLoader { private boolean needsUpdate = false; @@ -87,7 +87,7 @@ public class CustomToolConfig extends ConfigLoader { if (repairable) { byte repairData = (byte) config.getInt(toolType + "." + toolName + ".Repair_Material_Data_Value", -1); - int repairQuantity = Repair.getRepairAndSalvageQuantities(new ItemStack(toolMaterial), repairMaterial, repairData); + int repairQuantity = SkillUtils.getRepairAndSalvageQuantities(new ItemStack(toolMaterial), repairMaterial, repairData); if (repairQuantity == 0) { repairQuantity = config.getInt(toolType + "." + toolName + ".Repair_Material_Data_Quantity", 2); diff --git a/src/main/java/com/gmail/nossr50/config/skills/repair/RepairConfig.java b/src/main/java/com/gmail/nossr50/config/skills/repair/RepairConfig.java index 9622b49b9..3986a6c18 100644 --- a/src/main/java/com/gmail/nossr50/config/skills/repair/RepairConfig.java +++ b/src/main/java/com/gmail/nossr50/config/skills/repair/RepairConfig.java @@ -15,6 +15,7 @@ import com.gmail.nossr50.skills.repair.repairables.RepairMaterialType; import com.gmail.nossr50.skills.repair.repairables.Repairable; import com.gmail.nossr50.skills.repair.repairables.RepairableFactory; import com.gmail.nossr50.util.ItemUtils; +import com.gmail.nossr50.util.skills.SkillUtils; public class RepairConfig extends ConfigLoader { private List repairables; @@ -136,7 +137,7 @@ public class RepairConfig extends ConfigLoader { } // Minimum Quantity - int minimumQuantity = (itemMaterial != null ? Repair.getRepairAndSalvageQuantities(new ItemStack(itemMaterial), repairMaterial, repairMetadata) : config.getInt("Repairables." + key + ".MinimumQuantity", 2)); + int minimumQuantity = (itemMaterial != null ? SkillUtils.getRepairAndSalvageQuantities(new ItemStack(itemMaterial), repairMaterial, repairMetadata) : config.getInt("Repairables." + key + ".MinimumQuantity", 2)); if (minimumQuantity <= 0 && itemMaterial != null) { minimumQuantity = config.getInt("Repairables." + key + ".MinimumQuantity", 2); 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 e913b649d..eb6987ddc 100644 --- a/src/main/java/com/gmail/nossr50/datatypes/player/McMMOPlayer.java +++ b/src/main/java/com/gmail/nossr50/datatypes/player/McMMOPlayer.java @@ -41,6 +41,8 @@ import com.gmail.nossr50.skills.fishing.FishingManager; import com.gmail.nossr50.skills.herbalism.HerbalismManager; import com.gmail.nossr50.skills.mining.MiningManager; import com.gmail.nossr50.skills.repair.RepairManager; +import com.gmail.nossr50.skills.salvage.Salvage; +import com.gmail.nossr50.skills.salvage.SalvageManager; import com.gmail.nossr50.skills.smelting.SmeltingManager; import com.gmail.nossr50.skills.swords.SwordsManager; import com.gmail.nossr50.skills.taming.TamingManager; @@ -224,6 +226,10 @@ public class McMMOPlayer { return (RepairManager) skillManagers.get(SkillType.REPAIR); } + public SalvageManager getSalvageManager() { + return (SalvageManager) skillManagers.get(SkillType.SALVAGE); + } + public SmeltingManager getSmeltingManager() { return (SmeltingManager) skillManagers.get(SkillType.SMELTING); } diff --git a/src/main/java/com/gmail/nossr50/datatypes/skills/SecondaryAbility.java b/src/main/java/com/gmail/nossr50/datatypes/skills/SecondaryAbility.java index d1cc554c9..ee1563e8b 100644 --- a/src/main/java/com/gmail/nossr50/datatypes/skills/SecondaryAbility.java +++ b/src/main/java/com/gmail/nossr50/datatypes/skills/SecondaryAbility.java @@ -46,9 +46,12 @@ public enum SecondaryAbility { /* Repair */ ARCANE_FORGING, REPAIR_MASTERY, - SALVAGE, SUPER_REPAIR, + /* Salvage */ + ADVANCED_SALVAGE, + ARCANE_SALVAGE, + /* Smelting */ FLUX_MINING, FUEL_EFFICIENCY, diff --git a/src/main/java/com/gmail/nossr50/datatypes/skills/SkillType.java b/src/main/java/com/gmail/nossr50/datatypes/skills/SkillType.java index f16f43a58..4ce5730cb 100644 --- a/src/main/java/com/gmail/nossr50/datatypes/skills/SkillType.java +++ b/src/main/java/com/gmail/nossr50/datatypes/skills/SkillType.java @@ -23,6 +23,7 @@ import com.gmail.nossr50.skills.fishing.FishingManager; import com.gmail.nossr50.skills.herbalism.HerbalismManager; import com.gmail.nossr50.skills.mining.MiningManager; import com.gmail.nossr50.skills.repair.RepairManager; +import com.gmail.nossr50.skills.salvage.SalvageManager; import com.gmail.nossr50.skills.smelting.SmeltingManager; import com.gmail.nossr50.skills.swords.SwordsManager; import com.gmail.nossr50.skills.taming.TamingManager; @@ -43,7 +44,8 @@ public enum SkillType { FISHING(FishingManager.class, Color.NAVY, ImmutableList.of(SecondaryAbility.FISHERMANS_DIET, SecondaryAbility.FISHING_TREASURE_HUNTER, SecondaryAbility.ICE_FISHING, SecondaryAbility.MAGIC_HUNTER, SecondaryAbility.MASTER_ANGLER, SecondaryAbility.SHAKE)), HERBALISM(HerbalismManager.class, Color.GREEN, AbilityType.GREEN_TERRA, ToolType.HOE, ImmutableList.of(SecondaryAbility.FARMERS_DIET, SecondaryAbility.GREEN_THUMB_PLANT, SecondaryAbility.GREEN_THUMB_BLOCK, SecondaryAbility.HERBALISM_DOUBLE_DROPS, SecondaryAbility.HYLIAN_LUCK, SecondaryAbility.SHROOM_THUMB)), MINING(MiningManager.class, Color.GRAY, AbilityType.SUPER_BREAKER, ToolType.PICKAXE, ImmutableList.of(SecondaryAbility.MINING_DOUBLE_DROPS)), - REPAIR(RepairManager.class, Color.SILVER, ImmutableList.of(SecondaryAbility.ARCANE_FORGING, SecondaryAbility.REPAIR_MASTERY, SecondaryAbility.SALVAGE, SecondaryAbility.SUPER_REPAIR)), + REPAIR(RepairManager.class, Color.SILVER, ImmutableList.of(SecondaryAbility.ARCANE_FORGING, SecondaryAbility.REPAIR_MASTERY, SecondaryAbility.SUPER_REPAIR)), + SALVAGE(SalvageManager.class, Color.ORANGE, ImmutableList.of(SecondaryAbility.ADVANCED_SALVAGE, SecondaryAbility.ARCANE_SALVAGE)), SMELTING(SmeltingManager.class, Color.YELLOW, ImmutableList.of(SecondaryAbility.FLUX_MINING, SecondaryAbility.FUEL_EFFICIENCY, SecondaryAbility.SECOND_SMELT)), SWORDS(SwordsManager.class, Color.fromRGB(178, 34, 34), AbilityType.SERRATED_STRIKES, ToolType.SWORD, ImmutableList.of(SecondaryAbility.BLEED, SecondaryAbility.COUNTER)), TAMING(TamingManager.class, Color.PURPLE, ImmutableList.of(SecondaryAbility.BEAST_LORE, SecondaryAbility.CALL_OF_THE_WILD, SecondaryAbility.ENVIROMENTALLY_AWARE, SecondaryAbility.FAST_FOOD, SecondaryAbility.GORE, SecondaryAbility.HOLY_HOUND, SecondaryAbility.SHARPENED_CLAWS, SecondaryAbility.SHOCK_PROOF, SecondaryAbility.THICK_FUR)), @@ -182,6 +184,7 @@ public enum SkillType { // TODO: This is a little "hacky", we probably need to add something to distinguish child skills in the enum, or to use another enum for them public boolean isChildSkill() { switch (this) { + case SALVAGE: case SMELTING: return true; diff --git a/src/main/java/com/gmail/nossr50/listeners/BlockListener.java b/src/main/java/com/gmail/nossr50/listeners/BlockListener.java index edf821f76..fecf81c84 100644 --- a/src/main/java/com/gmail/nossr50/listeners/BlockListener.java +++ b/src/main/java/com/gmail/nossr50/listeners/BlockListener.java @@ -122,7 +122,9 @@ public class BlockListener implements Listener { } if (BlockUtils.isMcMMOAnvil(blockState)) { - UserManager.getPlayer(player).getRepairManager().placedAnvilCheck(blockState.getType()); + McMMOPlayer mcMMOPlayer = UserManager.getPlayer(player); + mcMMOPlayer.getRepairManager().placedAnvilCheck(blockState.getType()); + mcMMOPlayer.getSalvageManager().placedAnvilCheck(blockState.getType()); } } diff --git a/src/main/java/com/gmail/nossr50/listeners/PlayerListener.java b/src/main/java/com/gmail/nossr50/listeners/PlayerListener.java index bc67f955e..adbfdf1df 100644 --- a/src/main/java/com/gmail/nossr50/listeners/PlayerListener.java +++ b/src/main/java/com/gmail/nossr50/listeners/PlayerListener.java @@ -38,7 +38,6 @@ import com.gmail.nossr50.datatypes.chat.ChatMode; import com.gmail.nossr50.datatypes.party.Party; import com.gmail.nossr50.datatypes.player.McMMOPlayer; import com.gmail.nossr50.datatypes.skills.AbilityType; -import com.gmail.nossr50.datatypes.skills.SecondaryAbility; import com.gmail.nossr50.datatypes.skills.SkillType; import com.gmail.nossr50.locale.LocaleLoader; import com.gmail.nossr50.party.ShareHandler; @@ -49,6 +48,8 @@ import com.gmail.nossr50.skills.herbalism.HerbalismManager; import com.gmail.nossr50.skills.mining.MiningManager; import com.gmail.nossr50.skills.repair.Repair; import com.gmail.nossr50.skills.repair.RepairManager; +import com.gmail.nossr50.skills.salvage.Salvage; +import com.gmail.nossr50.skills.salvage.SalvageManager; import com.gmail.nossr50.skills.taming.TamingManager; import com.gmail.nossr50.skills.unarmed.Unarmed; import com.gmail.nossr50.util.BlockUtils; @@ -451,7 +452,7 @@ public class PlayerListener implements Listener { if (!Config.getInstance().getAbilitiesOnlyActivateWhenSneaking() || player.isSneaking()) { /* REPAIR CHECKS */ - if (type == Repair.repairAnvilMaterial && SkillType.REPAIR.getPermissions(player) && mcMMO.getRepairableManager().isRepairable(heldItem)) { + if (type == Repair.anvilMaterial && SkillType.REPAIR.getPermissions(player) && mcMMO.getRepairableManager().isRepairable(heldItem)) { RepairManager repairManager = mcMMOPlayer.getRepairManager(); event.setCancelled(true); @@ -462,13 +463,13 @@ public class PlayerListener implements Listener { } } /* SALVAGE CHECKS */ - else if (type == Repair.salvageAnvilMaterial && Permissions.secondaryAbilityEnabled(player, SecondaryAbility.SALVAGE) && Repair.isSalvageable(heldItem)) { - RepairManager repairManager = mcMMOPlayer.getRepairManager(); + else if (type == Salvage.anvilMaterial && SkillType.SALVAGE.getPermissions(player) && Salvage.isSalvageable(heldItem)) { + SalvageManager salvageManager = UserManager.getPlayer(player).getSalvageManager(); event.setCancelled(true); // Make sure the player knows what he's doing when trying to salvage an enchanted item - if (!(heldItem.getEnchantments().size() > 0) || repairManager.checkConfirmation(type, true)) { - repairManager.handleSalvage(block.getLocation(), heldItem); + if (!(heldItem.getEnchantments().size() > 0) || mcMMOPlayer.getRepairManager().checkConfirmation(type, true)) { + salvageManager.handleSalvage(block.getLocation(), heldItem); player.updateInventory(); } } @@ -490,22 +491,22 @@ public class PlayerListener implements Listener { if ((Config.getInstance().getAbilitiesOnlyActivateWhenSneaking() && player.isSneaking()) || !Config.getInstance().getAbilitiesOnlyActivateWhenSneaking()) { /* REPAIR CHECKS */ - if (type == Repair.repairAnvilMaterial && SkillType.REPAIR.getPermissions(player) && mcMMO.getRepairableManager().isRepairable(heldItem)) { + if (type == Repair.anvilMaterial && SkillType.REPAIR.getPermissions(player) && mcMMO.getRepairableManager().isRepairable(heldItem)) { RepairManager repairManager = mcMMOPlayer.getRepairManager(); // Cancel repairing an enchanted item if (repairManager.checkConfirmation(type, false) && Config.getInstance().getRepairConfirmRequired()) { - repairManager.setLastAnvilUse(Repair.repairAnvilMaterial, 0); + repairManager.setLastAnvilUse(Repair.anvilMaterial, 0); player.sendMessage(LocaleLoader.getString("Skills.Cancelled", LocaleLoader.getString("Repair.Pretty.Name"))); } } /* SALVAGE CHECKS */ - else if (type == Repair.salvageAnvilMaterial && Permissions.secondaryAbilityEnabled(player, SecondaryAbility.SALVAGE) && Repair.isSalvageable(heldItem)) { + else if (type == Salvage.anvilMaterial && SkillType.SALVAGE.getPermissions(player) && Salvage.isSalvageable(heldItem)) { RepairManager repairManager = mcMMOPlayer.getRepairManager(); // Cancel salvaging an enchanted item if (repairManager.checkConfirmation(type, false) && Config.getInstance().getRepairConfirmRequired()) { - repairManager.setLastAnvilUse(Repair.salvageAnvilMaterial, 0); + mcMMOPlayer.getSalvageManager().setLastAnvilUse(Repair.anvilMaterial, 0); player.sendMessage(LocaleLoader.getString("Skills.Cancelled", LocaleLoader.getString("Salvage.Pretty.Name"))); } } diff --git a/src/main/java/com/gmail/nossr50/skills/repair/Repair.java b/src/main/java/com/gmail/nossr50/skills/repair/Repair.java index 6ae2c034f..6ae035ba9 100644 --- a/src/main/java/com/gmail/nossr50/skills/repair/Repair.java +++ b/src/main/java/com/gmail/nossr50/skills/repair/Repair.java @@ -3,106 +3,14 @@ package com.gmail.nossr50.skills.repair; import java.util.List; import org.bukkit.Material; -import org.bukkit.inventory.ItemStack; -import org.bukkit.inventory.Recipe; -import org.bukkit.inventory.ShapedRecipe; -import org.bukkit.inventory.ShapelessRecipe; -import org.bukkit.material.MaterialData; -import com.gmail.nossr50.mcMMO; import com.gmail.nossr50.config.AdvancedConfig; import com.gmail.nossr50.config.Config; -import com.gmail.nossr50.locale.LocaleLoader; -import com.gmail.nossr50.util.ItemUtils; public class Repair { public static int repairMasteryMaxBonusLevel = AdvancedConfig.getInstance().getRepairMasteryMaxLevel(); public static double repairMasteryMaxBonus = AdvancedConfig.getInstance().getRepairMasteryMaxBonus(); - public static int salvageUnlockLevel = AdvancedConfig.getInstance().getSalvageUnlockLevel(); - - public static Material salvageAnvilMaterial = Config.getInstance().getSalvageAnvilMaterial(); - public static Material repairAnvilMaterial = Config.getInstance().getRepairAnvilMaterial(); + public static Material anvilMaterial = Config.getInstance().getRepairAnvilMaterial(); public static boolean anvilMessagesEnabled = Config.getInstance().getRepairAnvilMessagesEnabled(); - - /** - * Checks if the item is salvageable. - * - * @param item Item to check - * - * @return true if the item is salvageable, false otherwise - */ - public static boolean isSalvageable(ItemStack item) { - return (Config.getInstance().getSalvageTools() && ItemUtils.isMinecraftTool(item)) || (Config.getInstance().getSalvageArmor() && !ItemUtils.isChainmailArmor(item) && ItemUtils.isMinecraftArmor(item)); - } - - public static String getAnvilMessage(Material type) { - if (type == repairAnvilMaterial) { - return LocaleLoader.getString("Repair.Listener.Anvil"); - } - - if (type == salvageAnvilMaterial) { - return LocaleLoader.getString("Repair.Listener.Anvil2"); - } - - return ""; - } - - protected static Material getRepairAndSalvageItem(ItemStack inHand) { - if (ItemUtils.isDiamondTool(inHand) || ItemUtils.isDiamondArmor(inHand)) { - return Material.DIAMOND; - } - else if (ItemUtils.isGoldTool(inHand) || ItemUtils.isGoldArmor(inHand)) { - return Material.GOLD_INGOT; - } - else if (ItemUtils.isIronTool(inHand) || ItemUtils.isIronArmor(inHand)) { - return Material.IRON_INGOT; - } - else if (ItemUtils.isStoneTool(inHand)) { - return Material.COBBLESTONE; - } - else if (ItemUtils.isWoodTool(inHand)) { - return Material.WOOD; - } - else if (ItemUtils.isLeatherArmor(inHand)) { - return Material.LEATHER; - } - else if (ItemUtils.isStringTool(inHand)) { - return Material.STRING; - } - else { - return null; - } - } - - public static int getRepairAndSalvageQuantities(ItemStack item) { - return getRepairAndSalvageQuantities(item, getRepairAndSalvageItem(item), (byte) -1); - } - - public static int getRepairAndSalvageQuantities(ItemStack item, Material repairMaterial, byte repairMetadata) { - int quantity = 0; - MaterialData repairData = repairMaterial != null ? new MaterialData(repairMaterial, repairMetadata) : null; - List recipes = mcMMO.p.getServer().getRecipesFor(item); - - if (!recipes.isEmpty()) { - Recipe recipe = recipes.get(0); - - if (recipe instanceof ShapelessRecipe) { - for (ItemStack ingredient : ((ShapelessRecipe) recipe).getIngredientList()) { - if (ingredient != null && (repairMaterial == null || ingredient.getType() == repairMaterial) && (repairMetadata == -1 || ingredient.getData().equals(repairData))) { - quantity += ingredient.getAmount(); - } - } - } - else if (recipe instanceof ShapedRecipe) { - for (ItemStack ingredient : ((ShapedRecipe) recipe).getIngredientMap().values()) { - if (ingredient != null && (repairMaterial == null || ingredient.getType() == repairMaterial) && (repairMetadata == -1 || ingredient.getData().equals(repairData))) { - quantity += ingredient.getAmount(); - } - } - } - } - - return quantity; - } } diff --git a/src/main/java/com/gmail/nossr50/skills/repair/RepairManager.java b/src/main/java/com/gmail/nossr50/skills/repair/RepairManager.java index 0b289d8b0..c3e1d6489 100644 --- a/src/main/java/com/gmail/nossr50/skills/repair/RepairManager.java +++ b/src/main/java/com/gmail/nossr50/skills/repair/RepairManager.java @@ -3,7 +3,6 @@ package com.gmail.nossr50.skills.repair; import java.util.Map; import java.util.Map.Entry; -import org.bukkit.Location; import org.bukkit.Material; import org.bukkit.Sound; import org.bukkit.enchantments.Enchantment; @@ -23,6 +22,7 @@ import com.gmail.nossr50.locale.LocaleLoader; import com.gmail.nossr50.skills.SkillManager; import com.gmail.nossr50.skills.repair.ArcaneForging.Tier; import com.gmail.nossr50.skills.repair.repairables.Repairable; +import com.gmail.nossr50.skills.salvage.Salvage; import com.gmail.nossr50.util.EventUtils; import com.gmail.nossr50.util.Misc; import com.gmail.nossr50.util.Permissions; @@ -32,8 +32,6 @@ import com.gmail.nossr50.util.skills.SkillUtils; public class RepairManager extends SkillManager { private boolean placedRepairAnvil; private int lastRepairClick; - private boolean placedSalvageAnvil; - private int lastSalvageClick; public RepairManager(McMMOPlayer mcMMOPlayer) { super(mcMMOPlayer, SkillType.REPAIR); @@ -51,9 +49,7 @@ public class RepairManager extends SkillManager { return; } - if (Repair.anvilMessagesEnabled) { - player.sendMessage(Repair.getAnvilMessage(anvilType)); - } + player.sendMessage(LocaleLoader.getString("Repair.Listener.Anvil")); if (Config.getInstance().getRepairAnvilPlaceSoundsEnabled()) { player.playSound(player.getLocation(), Sound.ANVIL_LAND, Misc.ANVIL_USE_VOLUME, Misc.ANVIL_USE_PITCH); @@ -160,31 +156,6 @@ public class RepairManager extends SkillManager { return ((startDurability - newDurability) / (float) totalDurability); } - public void handleSalvage(Location location, ItemStack item) { - Player player = getPlayer(); - - if (getSkillLevel() < Repair.salvageUnlockLevel) { - player.sendMessage(LocaleLoader.getString("Repair.Skills.AdeptSalvage")); - return; - } - - if (item.getDurability() == 0) { - player.setItemInHand(new ItemStack(Material.AIR)); - location.setY(location.getY() + 1); - - Misc.dropItems(location, new ItemStack(Repair.getRepairAndSalvageItem(item)), Repair.getRepairAndSalvageQuantities(item) * item.getAmount()); - - if (Config.getInstance().getRepairAnvilUseSoundsEnabled()) { - player.playSound(player.getLocation(), Sound.ANVIL_USE, Misc.ANVIL_USE_VOLUME, Misc.ANVIL_USE_PITCH); - } - - player.sendMessage(LocaleLoader.getString("Repair.Skills.SalvageSuccess")); - } - else { - player.sendMessage(LocaleLoader.getString("Repair.Skills.NotFullDurability")); - } - } - /** * Check if the player has tried to use an Anvil before. * @@ -204,10 +175,10 @@ public class RepairManager extends SkillManager { actualizeLastAnvilUse(anvilType); - if (anvilType == Repair.repairAnvilMaterial) { + if (anvilType == Repair.anvilMaterial) { player.sendMessage(LocaleLoader.getString("Skills.ConfirmOrCancel", LocaleLoader.getString("Repair.Pretty.Name"))); } - else if (anvilType == Repair.salvageAnvilMaterial) { + else if (anvilType == Salvage.anvilMaterial) { player.sendMessage(LocaleLoader.getString("Skills.ConfirmOrCancel", LocaleLoader.getString("Salvage.Pretty.Name"))); } @@ -369,25 +340,17 @@ public class RepairManager extends SkillManager { */ public boolean getPlacedAnvil(Material anvilType) { - if (anvilType == Repair.repairAnvilMaterial) { + if (anvilType == Repair.anvilMaterial) { return placedRepairAnvil; } - if (anvilType == Repair.salvageAnvilMaterial) { - return placedSalvageAnvil; - } - return true; } public void togglePlacedAnvil(Material anvilType) { - if (anvilType == Repair.repairAnvilMaterial) { + if (anvilType == Repair.anvilMaterial) { placedRepairAnvil = !placedRepairAnvil; } - - if (anvilType == Repair.salvageAnvilMaterial) { - placedSalvageAnvil = !placedSalvageAnvil; - } } /* @@ -395,34 +358,22 @@ public class RepairManager extends SkillManager { */ public int getLastAnvilUse(Material anvilType) { - if (anvilType == Repair.repairAnvilMaterial) { + if (anvilType == Repair.anvilMaterial) { return lastRepairClick; } - if (anvilType == Repair.salvageAnvilMaterial) { - return lastSalvageClick; - } - return 0; } public void setLastAnvilUse(Material anvilType, int value) { - if (anvilType == Repair.repairAnvilMaterial) { + if (anvilType == Repair.anvilMaterial) { lastRepairClick = value; } - - if (anvilType == Repair.salvageAnvilMaterial) { - lastSalvageClick = value; - } } public void actualizeLastAnvilUse(Material anvilType) { - if (anvilType == Repair.repairAnvilMaterial) { + if (anvilType == Repair.anvilMaterial) { lastRepairClick = (int) (System.currentTimeMillis() / Misc.TIME_CONVERSION_FACTOR); } - - if (anvilType == Repair.salvageAnvilMaterial) { - lastSalvageClick = (int) (System.currentTimeMillis() / Misc.TIME_CONVERSION_FACTOR); - } } } diff --git a/src/main/java/com/gmail/nossr50/skills/salvage/Salvage.java b/src/main/java/com/gmail/nossr50/skills/salvage/Salvage.java new file mode 100644 index 000000000..b1cf4bb99 --- /dev/null +++ b/src/main/java/com/gmail/nossr50/skills/salvage/Salvage.java @@ -0,0 +1,111 @@ +package com.gmail.nossr50.skills.salvage; + +import org.bukkit.Material; +import org.bukkit.inventory.ItemStack; + +import com.gmail.nossr50.config.AdvancedConfig; +import com.gmail.nossr50.config.Config; +import com.gmail.nossr50.util.ItemUtils; +import com.gmail.nossr50.util.skills.SkillUtils; + +public class Salvage { + // The order of the values is extremely important, a few methods depend on it to work properly + public enum Tier { + EIGHT(8), + SEVEN(7), + SIX(6), + FIVE(5), + FOUR(4), + THREE(3), + TWO(2), + ONE(1); + + int numerical; + + private Tier(int numerical) { + this.numerical = numerical; + } + + public int toNumerical() { + return numerical; + } + + protected int getLevel() { + return AdvancedConfig.getInstance().getArcaneSalvageRankLevel(this); + } + + protected double getExtractFullEnchantChance() { + return AdvancedConfig.getInstance().getArcaneSalvageExtractFullEnchantsChance(this); + } + + protected double getExtractPartialEnchantChance() { + return AdvancedConfig.getInstance().getArcaneSalvageExtractPartialEnchantsChance(this); + } + } + + public static Material anvilMaterial = Config.getInstance().getSalvageAnvilMaterial(); + + public static int salvageMaxPercentageLevel = AdvancedConfig.getInstance().getSalvageMaxPercentageLevel(); + public static double salvageMaxPercentage = AdvancedConfig.getInstance().getSalvageMaxPercentage(); + + public static int advancedSalvageUnlockLevel = AdvancedConfig.getInstance().getAdvancedSalvageUnlockLevel(); + + public static boolean arcaneSalvageDowngrades = AdvancedConfig.getInstance().getArcaneSalvageEnchantDowngradeEnabled(); + public static boolean arcaneSalvageEnchantLoss = AdvancedConfig.getInstance().getArcaneSalvageEnchantLossEnabled(); + + /** + * Checks if the item is salvageable. + * + * @param item Item to check + * + * @return true if the item is salvageable, false otherwise + */ + public static boolean isSalvageable(ItemStack item) { + if (Config.getInstance().getSalvageTools() && ItemUtils.isMinecraftTool(item)) { + return true; + } + + if (Config.getInstance().getSalvageArmor() && !ItemUtils.isChainmailArmor(item) && ItemUtils.isMinecraftArmor(item)) { + return true; + } + + return false; + } + + protected static Material getSalvagedItem(ItemStack inHand) { + if (ItemUtils.isDiamondTool(inHand) || ItemUtils.isDiamondArmor(inHand)) { + return Material.DIAMOND; + } + else if (ItemUtils.isGoldTool(inHand) || ItemUtils.isGoldArmor(inHand)) { + return Material.GOLD_INGOT; + } + else if (ItemUtils.isIronTool(inHand) || ItemUtils.isIronArmor(inHand)) { + return Material.IRON_INGOT; + } + else if (ItemUtils.isStoneTool(inHand)) { + return Material.COBBLESTONE; + } + else if (ItemUtils.isWoodTool(inHand)) { + return Material.WOOD; + } + else if (ItemUtils.isLeatherArmor(inHand)) { + return Material.LEATHER; + } + else if (ItemUtils.isStringTool(inHand)) { + return Material.STRING; + } + else { + return null; + } + } + + protected static int getSalvagedAmount(ItemStack inHand) { + return SkillUtils.getRepairAndSalvageQuantities(inHand, getSalvagedItem(inHand), (byte) -1); + } + + protected static int calculateSalvageableAmount(short currentDurability, short maxDurability, int baseAmount) { + double percentDamaged = (double) (maxDurability - currentDurability) / maxDurability; + + return (int) Math.floor(baseAmount * percentDamaged); + } +} diff --git a/src/main/java/com/gmail/nossr50/skills/salvage/SalvageManager.java b/src/main/java/com/gmail/nossr50/skills/salvage/SalvageManager.java new file mode 100644 index 000000000..77e2c52ac --- /dev/null +++ b/src/main/java/com/gmail/nossr50/skills/salvage/SalvageManager.java @@ -0,0 +1,218 @@ +package com.gmail.nossr50.skills.salvage; + +import java.util.Map; +import java.util.Map.Entry; + +import org.bukkit.GameMode; +import org.bukkit.Location; +import org.bukkit.Material; +import org.bukkit.Sound; +import org.bukkit.enchantments.Enchantment; +import org.bukkit.entity.Player; +import org.bukkit.inventory.ItemStack; +import org.bukkit.inventory.meta.EnchantmentStorageMeta; + +import com.gmail.nossr50.datatypes.player.McMMOPlayer; +import com.gmail.nossr50.datatypes.skills.SkillType; +import com.gmail.nossr50.locale.LocaleLoader; +import com.gmail.nossr50.skills.SkillManager; +import com.gmail.nossr50.skills.salvage.Salvage.Tier; +import com.gmail.nossr50.util.Misc; +import com.gmail.nossr50.util.Permissions; + +public class SalvageManager extends SkillManager { + private boolean placedAnvil; + private int lastClick; + + public SalvageManager(McMMOPlayer mcMMOPlayer) { + super(mcMMOPlayer, SkillType.SALVAGE); + } + + /** + * Handles notifications for placing an anvil. + * + * @param anvilType The {@link Material} of the anvil block + */ + public void placedAnvilCheck(Material anvilType) { + Player player = getPlayer(); + + if (getPlacedAnvil(anvilType)) { + return; + } + + player.sendMessage(LocaleLoader.getString("Salvage.Listener.Anvil")); + + player.playSound(player.getLocation(), Sound.ANVIL_LAND, Misc.ANVIL_USE_VOLUME, Misc.ANVIL_USE_PITCH); + togglePlacedAnvil(anvilType); + } + + public void handleSalvage(Location location, ItemStack item) { + Player player = getPlayer(); + + if (player.getGameMode() != GameMode.SURVIVAL) { + return; + } + + if (item.getDurability() != 0 && (getSkillLevel() < Salvage.advancedSalvageUnlockLevel || !Permissions.advancedSalvage(player))) { + player.sendMessage(LocaleLoader.getString("Salvage.Skills.AdeptDamaged")); + return; + } + + int salvageableAmount = Salvage.calculateSalvageableAmount(item.getDurability(), item.getType().getMaxDurability(), Salvage.getSalvagedAmount(item)); + + if (salvageableAmount == 0) { + player.sendMessage(LocaleLoader.getString("Salvage.Skills.TooDamaged")); + return; + } + + double salvagePercentage = Math.min((((Salvage.salvageMaxPercentage / Salvage.salvageMaxPercentageLevel) * getSkillLevel()) / 100.0D), Salvage.salvageMaxPercentage / 100.0D); + salvageableAmount = Math.max((int) (salvageableAmount * salvagePercentage), 1); // Always get at least something back, if you're capable of repairing it. + + player.setItemInHand(new ItemStack(Material.AIR)); + location.add(0, 1, 0); + + Map enchants = item.getEnchantments(); + + if (!enchants.isEmpty()) { + ItemStack enchantBook = arcaneSalvageCheck(enchants); + + if (enchantBook != null) { + Misc.dropItem(location, enchantBook); + } + } + + Misc.dropItems(location, new ItemStack(Salvage.getSalvagedItem(item)), salvageableAmount); + + player.playSound(player.getLocation(), Sound.ANVIL_USE, Misc.ANVIL_USE_VOLUME, Misc.ANVIL_USE_PITCH); + player.sendMessage(LocaleLoader.getString("Repair.Skills.SalvageSuccess")); + } + + /** + * Gets the Arcane Salvage rank + * + * @return the current Arcane Salvage rank + */ + public int getArcaneSalvageRank() { + int skillLevel = getSkillLevel(); + + for (Tier tier : Tier.values()) { + if (skillLevel >= tier.getLevel()) { + return tier.toNumerical(); + } + } + + return 0; + } + + public double getExtractFullEnchantChance() { + int skillLevel = getSkillLevel(); + + for (Tier tier : Tier.values()) { + if (skillLevel >= tier.getLevel()) { + return tier.getExtractFullEnchantChance(); + } + } + + return 0; + } + + public double getExtractPartialEnchantChance() { + int skillLevel = getSkillLevel(); + + for (Tier tier : Tier.values()) { + if (skillLevel >= tier.getLevel()) { + return tier.getExtractPartialEnchantChance(); + } + } + + return 0; + } + + private ItemStack arcaneSalvageCheck(Map enchants) { + Player player = getPlayer(); + + if (getArcaneSalvageRank() == 0 || !Permissions.arcaneSalvage(player)) { + player.sendMessage(LocaleLoader.getString("Salvage.Skills.ArcaneFailed")); + return null; + } + + ItemStack book = new ItemStack(Material.ENCHANTED_BOOK); + EnchantmentStorageMeta enchantMeta = (EnchantmentStorageMeta) book.getItemMeta(); + + boolean downgraded = false; + + for (Entry enchant : enchants.entrySet()) { + int successChance = Misc.getRandom().nextInt(activationChance); + + if (!Salvage.arcaneSalvageEnchantLoss || getExtractFullEnchantChance() > successChance) { + enchantMeta.addStoredEnchant(enchant.getKey(), enchant.getValue(), true); + } + else if (enchant.getValue() > 1 && Salvage.arcaneSalvageDowngrades && getExtractPartialEnchantChance() > successChance) { + enchantMeta.addStoredEnchant(enchant.getKey(), enchant.getValue() - 1, true); + downgraded = true; + } + else { + downgraded = true; + } + } + + Map newEnchants = enchantMeta.getStoredEnchants(); + + if (newEnchants.isEmpty()) { + player.sendMessage(LocaleLoader.getString("Salvage.Skills.ArcaneFailed")); + return null; + } + + if (downgraded || newEnchants.size() < enchants.size()) { + player.sendMessage(LocaleLoader.getString("Salvage.Skills.ArcanePartial")); + } + else { + player.sendMessage(LocaleLoader.getString("Salvage.Skills.ArcaneSuccess")); + } + + book.setItemMeta(enchantMeta); + return book; + } + + /* + * Salvage Anvil Placement + */ + + public boolean getPlacedAnvil(Material anvilType) { + if (anvilType == Salvage.anvilMaterial) { + return placedAnvil; + } + + return true; + } + + public void togglePlacedAnvil(Material anvilType) { + if (anvilType == Salvage.anvilMaterial) { + placedAnvil = !placedAnvil; + } + } + + /* + * Salvage Anvil Usage + */ + + public int getLastAnvilUse(Material anvilType) { + if (anvilType == Salvage.anvilMaterial) { + return lastClick; + } + + return 0; + } + + public void setLastAnvilUse(Material anvilType, int value) { + if (anvilType == Salvage.anvilMaterial) { + lastClick = value; + } + } + + public void actualizeLastAnvilUse(Material anvilType) { + if (anvilType == Salvage.anvilMaterial) { + lastClick = (int) (System.currentTimeMillis() / Misc.TIME_CONVERSION_FACTOR); + } + } +} diff --git a/src/main/java/com/gmail/nossr50/util/BlockUtils.java b/src/main/java/com/gmail/nossr50/util/BlockUtils.java index ddb67bb98..30dbac2a1 100644 --- a/src/main/java/com/gmail/nossr50/util/BlockUtils.java +++ b/src/main/java/com/gmail/nossr50/util/BlockUtils.java @@ -14,6 +14,7 @@ import org.bukkit.material.SmoothBrick; import com.gmail.nossr50.mcMMO; import com.gmail.nossr50.skills.repair.Repair; +import com.gmail.nossr50.skills.salvage.Salvage; public final class BlockUtils { private BlockUtils() {} @@ -304,7 +305,7 @@ public final class BlockUtils { public static boolean isMcMMOAnvil(BlockState blockState) { Material type = blockState.getType(); - return type == Repair.repairAnvilMaterial || type == Repair.salvageAnvilMaterial; + return type == Repair.anvilMaterial || type == Salvage.anvilMaterial; } /** diff --git a/src/main/java/com/gmail/nossr50/util/Permissions.java b/src/main/java/com/gmail/nossr50/util/Permissions.java index 2947085ee..0b2b09294 100644 --- a/src/main/java/com/gmail/nossr50/util/Permissions.java +++ b/src/main/java/com/gmail/nossr50/util/Permissions.java @@ -162,6 +162,7 @@ public final class Permissions { public static boolean superBreaker(Permissible permissible) { return permissible.hasPermission("mcmmo.ability.mining.superbreaker"); } /* REPAIR */ + public static boolean repairArmor(Permissible permissible) { return permissible.hasPermission("mcmmo.ability.repair.armorrepair"); } public static boolean repairTools(Permissible permissible) { return permissible.hasPermission("mcmmo.ability.repair.toolrepair"); } public static boolean repairOtherItems(Permissible permissible) { return permissible.hasPermission("mcmmo.ability.repair.otherrepair"); } @@ -175,6 +176,14 @@ public final class Permissions { public static boolean repairStone(Permissible permissible) { return permissible.hasPermission("mcmmo.ability.repair.stonerepair"); } public static boolean repairWood(Permissible permissible) { return permissible.hasPermission("mcmmo.ability.repair.woodrepair"); } + /* SALVAGE */ + public static boolean advancedSalvage(Permissible permissible) { return permissible.hasPermission("mcmmo.ability.salvage.advancedsalvage"); } + public static boolean arcaneSalvage(Permissible permissible) { return permissible.hasPermission("mcmmo.ability.salvage.arcanesalvage"); } + + /* SMELTING */ + public static boolean fluxMining(Permissible permissible) { return permissible.hasPermission("mcmmo.ability.smelting.fluxmining"); } + public static boolean fuelEfficiency(Permissible permissible) { return permissible.hasPermission("mcmmo.ability.smelting.fuelefficiency"); } + /* SWORDS */ public static boolean serratedStrikes(Permissible permissible) { return permissible.hasPermission("mcmmo.ability.swords.serratedstrikes"); } diff --git a/src/main/java/com/gmail/nossr50/util/commands/CommandRegistrationManager.java b/src/main/java/com/gmail/nossr50/util/commands/CommandRegistrationManager.java index e23b4d452..bb9b9a432 100644 --- a/src/main/java/com/gmail/nossr50/util/commands/CommandRegistrationManager.java +++ b/src/main/java/com/gmail/nossr50/util/commands/CommandRegistrationManager.java @@ -43,6 +43,7 @@ import com.gmail.nossr50.commands.skills.FishingCommand; import com.gmail.nossr50.commands.skills.HerbalismCommand; import com.gmail.nossr50.commands.skills.MiningCommand; import com.gmail.nossr50.commands.skills.RepairCommand; +import com.gmail.nossr50.commands.skills.SalvageCommand; import com.gmail.nossr50.commands.skills.SmeltingCommand; import com.gmail.nossr50.commands.skills.SwordsCommand; import com.gmail.nossr50.commands.skills.TamingCommand; @@ -109,6 +110,10 @@ public final class CommandRegistrationManager { command.setExecutor(new RepairCommand()); break; + case SALVAGE: + command.setExecutor(new SalvageCommand()); + break; + case SMELTING: command.setExecutor(new SmeltingCommand()); break; diff --git a/src/main/java/com/gmail/nossr50/util/skills/SkillUtils.java b/src/main/java/com/gmail/nossr50/util/skills/SkillUtils.java index af702def0..c8dde01b7 100644 --- a/src/main/java/com/gmail/nossr50/util/skills/SkillUtils.java +++ b/src/main/java/com/gmail/nossr50/util/skills/SkillUtils.java @@ -8,7 +8,11 @@ import org.bukkit.Material; import org.bukkit.enchantments.Enchantment; import org.bukkit.entity.Player; import org.bukkit.inventory.ItemStack; +import org.bukkit.inventory.Recipe; +import org.bukkit.inventory.ShapedRecipe; +import org.bukkit.inventory.ShapelessRecipe; import org.bukkit.inventory.meta.ItemMeta; +import org.bukkit.material.MaterialData; import org.bukkit.potion.PotionEffect; import org.bukkit.potion.PotionEffectType; @@ -219,4 +223,62 @@ public class SkillUtils { return false; } + + protected static Material getRepairAndSalvageItem(ItemStack inHand) { + if (ItemUtils.isDiamondTool(inHand) || ItemUtils.isDiamondArmor(inHand)) { + return Material.DIAMOND; + } + else if (ItemUtils.isGoldTool(inHand) || ItemUtils.isGoldArmor(inHand)) { + return Material.GOLD_INGOT; + } + else if (ItemUtils.isIronTool(inHand) || ItemUtils.isIronArmor(inHand)) { + return Material.IRON_INGOT; + } + else if (ItemUtils.isStoneTool(inHand)) { + return Material.COBBLESTONE; + } + else if (ItemUtils.isWoodTool(inHand)) { + return Material.WOOD; + } + else if (ItemUtils.isLeatherArmor(inHand)) { + return Material.LEATHER; + } + else if (ItemUtils.isStringTool(inHand)) { + return Material.STRING; + } + else { + return null; + } + } + + public static int getRepairAndSalvageQuantities(ItemStack item) { + return getRepairAndSalvageQuantities(item, getRepairAndSalvageItem(item), (byte) -1); + } + + public static int getRepairAndSalvageQuantities(ItemStack item, Material repairMaterial, byte repairMetadata) { + int quantity = 0; + MaterialData repairData = repairMaterial != null ? new MaterialData(repairMaterial, repairMetadata) : null; + List recipes = mcMMO.p.getServer().getRecipesFor(item); + + if (!recipes.isEmpty()) { + Recipe recipe = recipes.get(0); + + if (recipe instanceof ShapelessRecipe) { + for (ItemStack ingredient : ((ShapelessRecipe) recipe).getIngredientList()) { + if (ingredient != null && (repairMaterial == null || ingredient.getType() == repairMaterial) && (repairMetadata == -1 || ingredient.getData().equals(repairData))) { + quantity += ingredient.getAmount(); + } + } + } + else if (recipe instanceof ShapedRecipe) { + for (ItemStack ingredient : ((ShapedRecipe) recipe).getIngredientMap().values()) { + if (ingredient != null && (repairMaterial == null || ingredient.getType() == repairMaterial) && (repairMetadata == -1 || ingredient.getData().equals(repairData))) { + quantity += ingredient.getAmount(); + } + } + } + } + + return quantity; + } } diff --git a/src/main/resources/advanced.yml b/src/main/resources/advanced.yml index 6d12541e5..e59222297 100644 --- a/src/main/resources/advanced.yml +++ b/src/main/resources/advanced.yml @@ -349,6 +349,55 @@ Skills: Rank_7: 15.0 Rank_8: 10.0 # + # Settings for Salvage + ### + Salvage: + # MaxPercentage: Maximum percentage of materials to be returned when Salvaging + # MaxPercentageLevel: On this level, the Salvage percentage will be + MaxPercentage: 100.0 + MaxPercentageLevel: 1000 + + # AdvancedSalvage_UnlockLevel: The level at which Advance Salvage become available + AdvancedSalvage: + UnlockLevel: 350 + + ArcaneSalvage: + # EnchantLossEnabled: When salvaging enchanted items, the enchants may be lost + # EnchantDowngradeEnabled: When salvaging enchanted items, the enchants may be downgraded + EnchantLossEnabled: true + EnchantDowngradeEnabled: true + Rank_Levels: + Rank_1: 125 + Rank_2: 250 + Rank_3: 375 + Rank_4: 500 + Rank_5: 625 + Rank_6: 750 + Rank_7: 875 + Rank_8: 1000 + + # ExtractFullEnchant: Chance to extract the full enchant at each ArcaneSalvage rank + ExtractFullEnchant: + Rank_1: 2.5 + Rank_2: 5.0 + Rank_3: 7.5 + Rank_4: 10.0 + Rank_5: 12.5 + Rank_6: 17.5 + Rank_7: 25.0 + Rank_8: 32.5 + + # ExtractPartialEnchant: Chance to extract the partial enchant at each ArcaneSalvage rank + ExtractPartialEnchant: + Rank_1: 2.0 + Rank_2: 2.5 + Rank_3: 5.0 + Rank_4: 7.5 + Rank_5: 10.0 + Rank_6: 12.5 + Rank_7: 15.0 + Rank_8: 17.5 + # # Settings for Smelting ### Smelting: diff --git a/src/main/resources/child.yml b/src/main/resources/child.yml index 84ef8340b..685a6ce71 100644 --- a/src/main/resources/child.yml +++ b/src/main/resources/child.yml @@ -8,6 +8,9 @@ # WARNING: THIS IS NOT SUPPORTED, IF YOU DO SO YOU ARE RESPONSIBLE FOR THE ISSUES THAT MAY ARISE. That said, watch out for circular dependencies, those are bad. # ##### +Salvage: + - Fishing + - Repair Smelting: - Mining - Repair \ No newline at end of file diff --git a/src/main/resources/config.yml b/src/main/resources/config.yml index 241410852..db7f3f8e6 100644 --- a/src/main/resources/config.yml +++ b/src/main/resources/config.yml @@ -309,11 +309,14 @@ Skills: Anvil_Placed_Sounds: true Anvil_Use_Sounds: true Anvil_Material: IRON_BLOCK + # Ask for a confirmation when a player tries to repair an enchanted item + Confirm_Required: true + Salvage: + Level_Cap: 0 + Anvil_Messages: true Salvage_Anvil_Material: GOLD_BLOCK Salvage_tools: true Salvage_armor: true - # Ask for a confirmation when a player tries to repair an enchanted item - Confirm_Required: true Smelting: Level_Cap: 0 Swords: @@ -449,4 +452,4 @@ Particles: # These settings determine if fireworks should get launched when a player levels-up, # this will happen by default for every 100 levels. LevelUp_Enabled: true - LevelUp_Tier: 100 \ No newline at end of file + LevelUp_Tier: 100 diff --git a/src/main/resources/locale/locale_en_US.properties b/src/main/resources/locale/locale_en_US.properties index 2631a697f..7aff57a19 100644 --- a/src/main/resources/locale/locale_en_US.properties +++ b/src/main/resources/locale/locale_en_US.properties @@ -234,14 +234,10 @@ Repair.Effect.6=Diamond Repair ({0}+ SKILL) Repair.Effect.7=Repair Diamond Tools & Armor Repair.Effect.8=Arcane Forging Repair.Effect.9=Repair magic items -Repair.Effect.16=Salvage ({0}+ SKILL) -Repair.Effect.17=Salvage Tools & Armor Repair.Error=[[DARK_RED]]mcMMO encountered an error attempting to repair this item! Repair.Listener.Anvil=[[DARK_RED]]You have placed an anvil, anvils can repair tools and armor. -Repair.Listener.Anvil2=[[DARK_RED]]You have placed a Salvage anvil, use this to Salvage tools and armor. Repair.Listener=Repair: Repair.SkillName=REPAIR -Repair.Skills.AdeptSalvage=[[DARK_RED]]You're not skilled enough to Salvage items. Repair.Skills.AdeptDiamond=[[DARK_RED]]You're not skilled enough to repair Diamond. Repair.Skills.AdeptGold=[[DARK_RED]]You're not skilled enough to repair Gold. Repair.Skills.AdeptIron=[[DARK_RED]]You're not skilled enough to repair Iron. @@ -249,14 +245,11 @@ Repair.Skills.AdeptStone=[[DARK_RED]]You're not skilled enough to repair Stone. Repair.Skills.Adept=[[RED]]You must be level [[YELLOW]]{0}[[RED]] to repair [[YELLOW]]{1} Repair.Skills.FeltEasy=[[GRAY]]That felt easy. Repair.Skills.FullDurability=[[GRAY]]That is at full durability. -Repair.Skills.SalvageSuccess=[[GRAY]]Item salvaged! -Repair.Skills.NotFullDurability=[[DARK_RED]]You can't salvage damaged items. Repair.Skills.Mastery=[[RED]]Repair Mastery: [[YELLOW]]Extra {0} durability restored Repair.Skills.StackedItems=[[DARK_RED]]You can't repair stacked items. Repair.Skills.Super.Chance=[[RED]]Super Repair Chance: [[YELLOW]]{0} Repair.Skillup=[[YELLOW]]Repair skill increased by {0}. Total ({1}) Repair.Pretty.Name=Repair -Salvage.Pretty.Name=Salvage #Arcane Forging Repair.Arcane.Chance.Downgrade=[[GRAY]]AF Downgrade Chance: [[YELLOW]]{0}% @@ -267,6 +260,27 @@ Repair.Arcane.Lost=[[RED]]You were not skilled enough to keep any enchantments. Repair.Arcane.Perfect=[[GREEN]]You have sustained the arcane energies in this item. Repair.Arcane.Rank=[[RED]]Arcane Forging: [[YELLOW]]Rank {0}/{1} +#SALVAGE +Salvage.Pretty.Name=Salvage +Salvage.Effect.0=Advanced Salvage +Salvage.Effect.1=Salvage damaged items +Salvage.Effect.2=Arcane Salvaging +Salvage.Effect.3=Extract enchantments from items +Salvage.Ability.Locked.0=LOCKED UNTIL {0}+ SKILL (ADVANCED SALVAGE) +Salvage.Ability.Bonus.0=Advanced Salvage +Salvage.Ability.Bonus.1=Salvaging damaged items will grant you items accordingly +Salvage.Arcane.Rank=[[RED]]Arcane Salvaging: [[YELLOW]]Rank {0}/{1} +Salvage.Arcane.ExtractFull=[[GRAY]]AS Full-Enchant Chance +Salvage.Arcane.ExtractPartial=[[GRAY]]AS Partial-Enchant Chance +Salvage.Skills.AdeptDamaged=You aren't skilled enough to salvage damaged items. +Salvage.Skills.TooDamaged=This item is too damaged to be salvaged. +Salvage.Skills.ArcaneFailed=You were unable to extract the knowledge contained within this item. +Salvage.Skills.ArcanePartial=You were only able to extract some of the knowledge contained within this item. +Salvage.Skills.ArcaneSuccess=You able to extract all of the knowledge contained within this item! +Salvage.Listener.Anvil=[[DARK_RED]]You have placed a Salvage anvil, use this to Salvage tools and armor. +Salvage.Listener=Salvage: +Salvage.SkillName=SALVAGE + #SWORDS Swords.Ability.Lower=[[GRAY]]**YOU LOWER YOUR SWORD** Swords.Ability.Ready=[[GREEN]]**YOU READY YOUR SWORD** diff --git a/src/main/resources/plugin.yml b/src/main/resources/plugin.yml index 365348e64..0fedfe663 100644 --- a/src/main/resources/plugin.yml +++ b/src/main/resources/plugin.yml @@ -91,6 +91,8 @@ commands: description: Detailed mcMMO skill info alchemy: description: Detailed mcMMO skill info + salvage: + description: Detailed mcMMO skill info adminchat: aliases: [ac, a] description: Toggle Admin chat or send admin chat messages @@ -152,6 +154,7 @@ permissions: mcmmo.ability.herbalism.all: true mcmmo.ability.mining.all: true mcmmo.ability.repair.all: true + mcmmo.ability.salvage.all: true mcmmo.ability.smelting.all: true mcmmo.ability.swords.all: true mcmmo.ability.taming.all: true @@ -433,6 +436,20 @@ permissions: description: Allows ability to repair tools mcmmo.ability.repair.woodrepair: description: Allows ability to repair Wood tools + mcmmo.ability.salvage.*: + default: false + description: Allows access to all Salvage abilities + children: + mcmmo.ability.salvage.all: true + mcmmo.ability.salvage.all: + description: Allows access to all Smelting abilities + children: + mcmmo.ability.salvage.advancedsalvage: true + mcmmo.ability.salvage.arcanesalvage: true + mcmmo.ability.salvage.advancedsalvage: + description: Allows access to the Advanced Salvage ability + mcmmo.ability.salvage.arcanesalvage: + description: Allows access to the Arcane Salvage ability mcmmo.ability.smelting.*: default: false description: Allows access to all Smelting abilities @@ -656,6 +673,7 @@ permissions: mcmmo.commands.party.all: true mcmmo.commands.ptp.all: true mcmmo.commands.repair: true + mcmmo.commands.salvage: true mcmmo.commands.smelting: true mcmmo.commands.swords: true mcmmo.commands.taming: true @@ -844,6 +862,7 @@ permissions: mcmmo.commands.mctop.herbalism: true mcmmo.commands.mctop.mining: true mcmmo.commands.mctop.repair: true + mcmmo.commands.mctop.salvage: true mcmmo.commands.mctop.smelting: true mcmmo.commands.mctop.swords: true mcmmo.commands.mctop.taming: true @@ -869,6 +888,8 @@ permissions: description: Allows access to the mctop command for mining mcmmo.commands.mctop.repair: description: Allows access to the mctop command for repair + mcmmo.commands.mctop.salvage: + description: Allows access to the mctop command for salvage mcmmo.commands.mctop.smelting: description: Allows access to the mctop command for smelting mcmmo.commands.mctop.swords: @@ -1014,6 +1035,7 @@ permissions: mcmmo.commands.skillreset.mining: true mcmmo.commands.skillreset.others.all: true mcmmo.commands.skillreset.repair: true + mcmmo.commands.skillreset.salvage: true mcmmo.commands.skillreset.smelting: true mcmmo.commands.skillreset.swords: true mcmmo.commands.skillreset.taming: true @@ -1055,6 +1077,7 @@ permissions: mcmmo.commands.skillreset.others.herbalism: true mcmmo.commands.skillreset.others.mining: true mcmmo.commands.skillreset.others.repair: true + mcmmo.commands.skillreset.others.salvage: true mcmmo.commands.skillreset.others.smelting: true mcmmo.commands.skillreset.others.swords: true mcmmo.commands.skillreset.others.taming: true @@ -1080,6 +1103,8 @@ permissions: description: Allows access to the skillreset command for mining for other players mcmmo.commands.skillreset.others.repair: description: Allows access to the skillreset command for repair for other players + mcmmo.commands.skillreset.others.salvage: + description: Allows access to the skillreset command for salvage for other players mcmmo.commands.skillreset.others.smelting: description: Allows access to the skillreset command for smelting for other players mcmmo.commands.skillreset.others.swords: @@ -1092,6 +1117,8 @@ permissions: description: Allows access to the skillreset command for woodcutting for other players mcmmo.commands.skillreset.repair: description: Allows access to the skillreset command for repair + mcmmo.commands.skillreset.salvage: + description: Allows access to the skillreset command for smelting mcmmo.commands.skillreset.smelting: description: Allows access to the skillreset command for smelting mcmmo.commands.skillreset.swords: @@ -1102,6 +1129,8 @@ permissions: description: Allows access to the skillreset command for unarmed mcmmo.commands.skillreset.woodcutting: description: Allows access to the skillreset command for woodcutting + mcmmo.commands.salvage: + description: Allows access to the salvage command mcmmo.commands.smelting: description: Allows access to the smelting command mcmmo.commands.swords: @@ -1264,6 +1293,7 @@ permissions: mcmmo.perks.lucky.herbalism: true mcmmo.perks.lucky.mining: true mcmmo.perks.lucky.repair: true + mcmmo.perks.lucky.salvage: true mcmmo.perks.lucky.smelting: true mcmmo.perks.lucky.swords: true mcmmo.perks.lucky.taming: true @@ -1296,6 +1326,9 @@ permissions: mcmmo.perks.lucky.repair: default: false description: Gives Repair abilities & skills a 33.3% better chance to activate. + mcmmo.perks.lucky.salvage: + default: false + description: Gives Salvage abilities & skills a 33.3% better chance to activate. mcmmo.perks.lucky.smelting: default: false description: Gives Smelting abilities & skills a 33.3% better chance to activate. @@ -1840,6 +1873,7 @@ permissions: mcmmo.skills.herbalism: true mcmmo.skills.mining: true mcmmo.skills.repair: true + mcmmo.skills.salvage: true mcmmo.skills.swords: true mcmmo.skills.smelting: true mcmmo.skills.taming: true