SkillUtils.java 23 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508
  1. package com.gmail.nossr50.util.skills;
  2. import com.gmail.nossr50.config.AdvancedConfig;
  3. import com.gmail.nossr50.config.Config;
  4. import com.gmail.nossr50.config.HiddenConfig;
  5. import com.gmail.nossr50.datatypes.interactions.NotificationType;
  6. import com.gmail.nossr50.datatypes.player.McMMOPlayer;
  7. import com.gmail.nossr50.datatypes.skills.SubSkillType;
  8. import com.gmail.nossr50.datatypes.skills.SuperAbility;
  9. import com.gmail.nossr50.datatypes.skills.PrimarySkill;
  10. import com.gmail.nossr50.datatypes.skills.XPGainReason;
  11. import com.gmail.nossr50.datatypes.skills.subskills.AbstractSubSkill;
  12. import com.gmail.nossr50.datatypes.skills.subskills.interfaces.RandomChance;
  13. import com.gmail.nossr50.events.skills.secondaryabilities.SubSkillEvent;
  14. import com.gmail.nossr50.events.skills.secondaryabilities.SubSkillWeightedActivationCheckEvent;
  15. import com.gmail.nossr50.listeners.InteractionManager;
  16. import com.gmail.nossr50.locale.LocaleLoader;
  17. import com.gmail.nossr50.mcMMO;
  18. import com.gmail.nossr50.util.EventUtils;
  19. import com.gmail.nossr50.util.ItemUtils;
  20. import com.gmail.nossr50.util.Misc;
  21. import com.gmail.nossr50.util.StringUtils;
  22. import com.gmail.nossr50.util.player.UserManager;
  23. import org.bukkit.Location;
  24. import org.bukkit.Material;
  25. import org.bukkit.enchantments.Enchantment;
  26. import org.bukkit.entity.Player;
  27. import org.bukkit.inventory.ItemStack;
  28. import org.bukkit.inventory.Recipe;
  29. import org.bukkit.inventory.ShapedRecipe;
  30. import org.bukkit.inventory.ShapelessRecipe;
  31. import org.bukkit.inventory.meta.ItemMeta;
  32. import org.bukkit.potion.PotionEffect;
  33. import org.bukkit.potion.PotionEffectType;
  34. import java.awt.*;
  35. import java.text.DecimalFormat;
  36. import java.util.ArrayList;
  37. import java.util.List;
  38. public class SkillUtils {
  39. public static final DecimalFormat percent = new DecimalFormat("##0.00%");
  40. public static final DecimalFormat decimal = new DecimalFormat("##0.00");
  41. public static void applyXpGain(McMMOPlayer mcMMOPlayer, PrimarySkill skill, float xp, XPGainReason xpGainReason) {
  42. mcMMOPlayer.beginXpGain(skill, xp, xpGainReason);
  43. }
  44. /*
  45. * Skill Stat Calculations
  46. */
  47. public static String[] calculateAbilityDisplayValues(double chance, boolean isLucky) {
  48. String[] displayValues = new String[2];
  49. displayValues[0] = percent.format(Math.min(chance, 100.0D) / 100.0D);
  50. displayValues[1] = isLucky ? percent.format(Math.min(chance * 1.3333D, 100.0D) / 100.0D) : null;
  51. return displayValues;
  52. }
  53. public static String[] calculateAbilityDisplayValues(float skillValue, SubSkillType subSkillType, boolean isLucky) {
  54. int maxBonusLevel = AdvancedConfig.getInstance().getMaxBonusLevel(subSkillType);
  55. return calculateAbilityDisplayValues((AdvancedConfig.getInstance().getMaxChance(subSkillType) / maxBonusLevel) * Math.min(skillValue, maxBonusLevel), isLucky);
  56. }
  57. public static String[] calculateLengthDisplayValues(Player player, float skillValue, PrimarySkill skill) {
  58. int maxLength = skill.getAbility().getMaxLength();
  59. int length = 2 + (int) (skillValue / AdvancedConfig.getInstance().getAbilityLength());
  60. int enduranceLength = PerksUtils.handleActivationPerks(player, length, maxLength);
  61. if (maxLength != 0) {
  62. length = Math.min(length, maxLength);
  63. }
  64. return new String[] { String.valueOf(length), String.valueOf(enduranceLength) };
  65. }
  66. /*
  67. * Others
  68. */
  69. public static int handleFoodSkills(Player player, PrimarySkill skill, int eventFoodLevel, int baseLevel, int maxLevel, int rankChange) {
  70. int skillLevel = UserManager.getPlayer(player).getSkillLevel(skill);
  71. int currentFoodLevel = player.getFoodLevel();
  72. int foodChange = eventFoodLevel - currentFoodLevel;
  73. for (int i = baseLevel; i <= maxLevel; i += rankChange) {
  74. if (skillLevel >= i) {
  75. foodChange++;
  76. }
  77. }
  78. return currentFoodLevel + foodChange;
  79. }
  80. /**
  81. * Calculate the time remaining until the cooldown expires.
  82. *
  83. * @param deactivatedTimeStamp Time of deactivation
  84. * @param cooldown The length of the cooldown
  85. * @param player The Player to check for cooldown perks
  86. *
  87. * @return the number of seconds remaining before the cooldown expires
  88. */
  89. public static int calculateTimeLeft(long deactivatedTimeStamp, int cooldown, Player player) {
  90. return (int) (((deactivatedTimeStamp + (PerksUtils.handleCooldownPerks(player, cooldown) * Misc.TIME_CONVERSION_FACTOR)) - System.currentTimeMillis()) / Misc.TIME_CONVERSION_FACTOR);
  91. }
  92. /**
  93. * Check if the cooldown has expired.
  94. * This does NOT account for cooldown perks!
  95. *
  96. * @param deactivatedTimeStamp Time of deactivation in seconds
  97. * @param cooldown The length of the cooldown in seconds
  98. *
  99. * @return true if the cooldown is expired
  100. */
  101. public static boolean cooldownExpired(long deactivatedTimeStamp, int cooldown) {
  102. return System.currentTimeMillis() >= (deactivatedTimeStamp + cooldown) * Misc.TIME_CONVERSION_FACTOR;
  103. }
  104. /**
  105. * Checks if the given string represents a valid skill
  106. *
  107. * @param skillName The name of the skill to check
  108. * @return true if this is a valid skill, false otherwise
  109. */
  110. public static boolean isSkill(String skillName) {
  111. return Config.getInstance().getLocale().equalsIgnoreCase("en_US") ? PrimarySkill.getSkill(skillName) != null : isLocalizedSkill(skillName);
  112. }
  113. public static void sendSkillMessage(Player player, NotificationType notificationType, String key) {
  114. Location location = player.getLocation();
  115. for (Player otherPlayer : player.getWorld().getPlayers()) {
  116. if (otherPlayer != player && Misc.isNear(location, otherPlayer.getLocation(), Misc.SKILL_MESSAGE_MAX_SENDING_DISTANCE)) {
  117. InteractionManager.sendOtherPlayersSkillInfo(player, notificationType, key);
  118. }
  119. }
  120. }
  121. public static void handleAbilitySpeedIncrease(Player player) {
  122. if (HiddenConfig.getInstance().useEnchantmentBuffs()) {
  123. ItemStack heldItem = player.getInventory().getItemInMainHand();
  124. if (heldItem == null || heldItem.getType() == Material.AIR) {
  125. return;
  126. }
  127. int efficiencyLevel = heldItem.getEnchantmentLevel(Enchantment.DIG_SPEED);
  128. ItemMeta itemMeta = heldItem.getItemMeta();
  129. List<String> itemLore = new ArrayList<String>();
  130. if (itemMeta.hasLore()) {
  131. itemLore = itemMeta.getLore();
  132. }
  133. itemLore.add("mcMMO Ability Tool");
  134. itemMeta.addEnchant(Enchantment.DIG_SPEED, efficiencyLevel + AdvancedConfig.getInstance().getEnchantBuff(), true);
  135. itemMeta.setLore(itemLore);
  136. heldItem.setItemMeta(itemMeta);
  137. }
  138. else {
  139. int duration = 0;
  140. int amplifier = 0;
  141. if (player.hasPotionEffect(PotionEffectType.FAST_DIGGING)) {
  142. for (PotionEffect effect : player.getActivePotionEffects()) {
  143. if (effect.getType() == PotionEffectType.FAST_DIGGING) {
  144. duration = effect.getDuration();
  145. amplifier = effect.getAmplifier();
  146. break;
  147. }
  148. }
  149. }
  150. McMMOPlayer mcMMOPlayer = UserManager.getPlayer(player);
  151. PrimarySkill skill = mcMMOPlayer.getAbilityMode(SuperAbility.SUPER_BREAKER) ? PrimarySkill.MINING : PrimarySkill.EXCAVATION;
  152. int ticks = PerksUtils.handleActivationPerks(player, 2 + (mcMMOPlayer.getSkillLevel(skill) / AdvancedConfig.getInstance().getAbilityLength()), skill.getAbility().getMaxLength()) * Misc.TICK_CONVERSION_FACTOR;
  153. PotionEffect abilityBuff = new PotionEffect(PotionEffectType.FAST_DIGGING, duration + ticks, amplifier + 10);
  154. player.addPotionEffect(abilityBuff, true);
  155. }
  156. }
  157. public static void handleAbilitySpeedDecrease(Player player) {
  158. if (!HiddenConfig.getInstance().useEnchantmentBuffs()) {
  159. return;
  160. }
  161. for (ItemStack item : player.getInventory().getContents()) {
  162. removeAbilityBuff(item);
  163. }
  164. }
  165. public static void removeAbilityBuff(ItemStack item) {
  166. if (item == null || item.getType() == Material.AIR || (!ItemUtils.isPickaxe(item) && !ItemUtils.isShovel(item)) || !item.containsEnchantment(Enchantment.DIG_SPEED)) {
  167. return;
  168. }
  169. ItemMeta itemMeta = item.getItemMeta();
  170. if (itemMeta.hasLore()) {
  171. List<String> itemLore = itemMeta.getLore();
  172. if (itemLore.remove("mcMMO Ability Tool")) {
  173. int efficiencyLevel = item.getEnchantmentLevel(Enchantment.DIG_SPEED);
  174. if (efficiencyLevel <= AdvancedConfig.getInstance().getEnchantBuff()) {
  175. itemMeta.removeEnchant(Enchantment.DIG_SPEED);
  176. }
  177. else {
  178. itemMeta.addEnchant(Enchantment.DIG_SPEED, efficiencyLevel - AdvancedConfig.getInstance().getEnchantBuff(), true);
  179. }
  180. itemMeta.setLore(itemLore);
  181. item.setItemMeta(itemMeta);
  182. }
  183. }
  184. }
  185. public static void handleDurabilityChange(ItemStack itemStack, int durabilityModifier) {
  186. handleDurabilityChange(itemStack, durabilityModifier, 1.0);
  187. }
  188. /**
  189. * Modify the durability of an ItemStack.
  190. *
  191. * @param itemStack The ItemStack which durability should be modified
  192. * @param durabilityModifier the amount to modify the durability by
  193. * @param maxDamageModifier the amount to adjust the max damage by
  194. */
  195. public static void handleDurabilityChange(ItemStack itemStack, int durabilityModifier, double maxDamageModifier) {
  196. if (itemStack.hasItemMeta() && itemStack.getItemMeta().isUnbreakable()) {
  197. return;
  198. }
  199. Material type = itemStack.getType();
  200. short maxDurability = mcMMO.getRepairableManager().isRepairable(type) ? mcMMO.getRepairableManager().getRepairable(type).getMaximumDurability() : type.getMaxDurability();
  201. durabilityModifier = (int) Math.min(durabilityModifier / (itemStack.getEnchantmentLevel(Enchantment.DURABILITY) + 1), maxDurability * maxDamageModifier);
  202. itemStack.setDurability((short) Math.min(itemStack.getDurability() + durabilityModifier, maxDurability));
  203. }
  204. /**
  205. * Checks whether or not the given skill succeeds
  206. * @param subSkillType The ability corresponding to this check
  207. * @param player The player whose skill levels we are checking against
  208. * @param skillLevel The skill level of the corresponding skill
  209. * @param activationChance used to determine activation chance
  210. * @param maxChance maximum chance
  211. * @param maxLevel maximum skill level bonus
  212. * @return true if random chance succeeds and the event isn't cancelled
  213. */
  214. private static boolean performRandomSkillCheck(SubSkillType subSkillType, Player player, int skillLevel, int activationChance, double maxChance, int maxLevel) {
  215. double chance = (maxChance / maxLevel) * Math.min(skillLevel, maxLevel) / activationChance;
  216. return performRandomSkillCheckStatic(subSkillType, player, activationChance, chance);
  217. }
  218. /* NEW VERSION */
  219. private static boolean performRandomSkillCheck(AbstractSubSkill abstractSubSkill, Player player, int skillLevel, int activationChance, double maxChance, int maxLevel) {
  220. double chance = (maxChance / maxLevel) * Math.min(skillLevel, maxLevel) / activationChance;
  221. return performRandomSkillCheckStatic(abstractSubSkill, player, activationChance, chance);
  222. }
  223. /**
  224. * This method is the final step in determining if a Sub-Skill / Secondary Skill in mcMMO successfully activates either from chance or otherwise
  225. *
  226. * There are 4 types of Sub-Skill / Secondary Skill activations in mcMMO
  227. * 1) Random Chance with a linear increase to 100% (At 100 Skill Level)
  228. * 2) Random Chance with a linear increase to 100% at 100 Skill Level but caps out earlier in the curve (At x/100 Skill Level)
  229. * 3) Random Chance with a pre-determined activation roll and threshold roll
  230. * 4) Skills that are not chance based
  231. *
  232. * Random skills check for success based on numbers and then fire a cancellable event, if that event is not cancelled they succeed
  233. * All other skills just fire the cancellable event and succeed if it is not cancelled
  234. *
  235. * @param subSkillType The identifier for this specific sub-skill
  236. * @param player The owner of this sub-skill
  237. * @param skill The identifier for the parent of our sub-skill
  238. * @param activationChance This is the value that we roll against, 100 is normal, and 75 is for lucky perk
  239. * @param subskillActivationType this value represents what kind of activation procedures this sub-skill uses
  240. * @return returns true if all conditions are met and they event is not cancelled
  241. */
  242. public static boolean isActivationSuccessful(SkillActivationType subskillActivationType, SubSkillType subSkillType, Player player,
  243. PrimarySkill skill, int skillLevel, int activationChance)
  244. {
  245. //Maximum chance to succeed
  246. double maxChance = AdvancedConfig.getInstance().getMaxChance(subSkillType);
  247. //Maximum roll we can make
  248. int maxBonusLevel = AdvancedConfig.getInstance().getMaxBonusLevel(subSkillType);
  249. switch(subskillActivationType)
  250. {
  251. //100 Skill = Guaranteed
  252. case RANDOM_LINEAR_100_SCALE_NO_CAP:
  253. return performRandomSkillCheck(subSkillType, player, skillLevel, PerksUtils.handleLuckyPerks(player, skill), 100.0D, 100);
  254. case RANDOM_LINEAR_100_SCALE_WITH_CAP:
  255. return performRandomSkillCheck(subSkillType, player, skillLevel, PerksUtils.handleLuckyPerks(player, skill), maxChance, maxBonusLevel);
  256. case RANDOM_STATIC_CHANCE:
  257. //Grab the static activation chance of this skill
  258. double staticRoll = getSecondaryAbilityStaticChance(subSkillType) / activationChance;
  259. return performRandomSkillCheckStatic(subSkillType, player, activationChance, staticRoll);
  260. case ALWAYS_FIRES:
  261. SubSkillEvent event = EventUtils.callSubSkillEvent(player, subSkillType);
  262. return !event.isCancelled();
  263. default:
  264. return false;
  265. }
  266. }
  267. public static double getChanceOfSuccess(int skillLevel, double maxLevelBonus, double curve)
  268. {
  269. return getChanceOfSuccess((double) skillLevel, maxLevelBonus, curve);
  270. }
  271. public static double getChanceOfSuccess(double skillLevel, double maxLevelBonus, double curve)
  272. {
  273. if(skillLevel > maxLevelBonus)
  274. return maxLevelBonus / curve;
  275. return skillLevel / curve;
  276. }
  277. /* NEW VERSION */
  278. public static boolean isActivationSuccessful(SkillActivationType skillActivationType, AbstractSubSkill abstractSubSkill, Player player, double maxChance, int maxBonusLevel)
  279. {
  280. int skillLevel = UserManager.getPlayer(player).getSkillLevel(abstractSubSkill.getPrimarySkill());
  281. PrimarySkill skill = abstractSubSkill.getPrimarySkill();
  282. switch(skillActivationType)
  283. {
  284. //100 Skill = Guaranteed
  285. case RANDOM_LINEAR_100_SCALE_NO_CAP:
  286. return performRandomSkillCheck(abstractSubSkill, player, skillLevel, PerksUtils.handleLuckyPerks(player, skill), 100.0D, 100);
  287. case RANDOM_LINEAR_100_SCALE_WITH_CAP:
  288. return performRandomSkillCheck(abstractSubSkill, player, skillLevel, PerksUtils.handleLuckyPerks(player, skill), maxChance, maxBonusLevel);
  289. case RANDOM_STATIC_CHANCE:
  290. //TODO: Add this in for the new system
  291. //Grab the static activation chance of this skill
  292. //double staticRoll = getSecondaryAbilityStaticChance(subSkillType) / activationChance;
  293. //return performRandomSkillCheckStatic(subSkillType, player, activationChance, staticRoll);
  294. return false;
  295. case ALWAYS_FIRES:
  296. SubSkillEvent event = EventUtils.callSubSkillEvent(player, abstractSubSkill);
  297. return !event.isCancelled();
  298. default:
  299. return false;
  300. }
  301. }
  302. public static boolean isActivationSuccessful(SkillActivationType skillActivationType, AbstractSubSkill abstractSubSkill, Player player)
  303. {
  304. //Maximum chance to succeed
  305. RandomChance randomChance = (RandomChance) abstractSubSkill;
  306. double maxChance = randomChance.getRandomChanceMaxChance();
  307. //Maximum roll we can make
  308. int maxBonusLevel = randomChance.getRandomChanceMaxBonus();
  309. int skillLevel = UserManager.getPlayer(player).getSkillLevel(abstractSubSkill.getPrimarySkill());
  310. PrimarySkill skill = abstractSubSkill.getPrimarySkill();
  311. switch(skillActivationType)
  312. {
  313. //100 Skill = Guaranteed
  314. case RANDOM_LINEAR_100_SCALE_NO_CAP:
  315. return performRandomSkillCheck(abstractSubSkill, player, skillLevel, PerksUtils.handleLuckyPerks(player, skill), 100.0D, 100);
  316. case RANDOM_LINEAR_100_SCALE_WITH_CAP:
  317. return performRandomSkillCheck(abstractSubSkill, player, skillLevel, PerksUtils.handleLuckyPerks(player, skill), maxChance, maxBonusLevel);
  318. case RANDOM_STATIC_CHANCE:
  319. //TODO: Add this in for the new system
  320. //Grab the static activation chance of this skill
  321. //double staticRoll = getSecondaryAbilityStaticChance(subSkillType) / activationChance;
  322. //return performRandomSkillCheckStatic(subSkillType, player, activationChance, staticRoll);
  323. return false;
  324. case ALWAYS_FIRES:
  325. SubSkillEvent event = EventUtils.callSubSkillEvent(player, abstractSubSkill);
  326. return !event.isCancelled();
  327. default:
  328. return false;
  329. }
  330. }
  331. /**
  332. * Grabs static activation rolls for Secondary Abilities
  333. * @param subSkillType The secondary ability to grab properties of
  334. * @return The static activation roll involved in the RNG calculation
  335. */
  336. public static double getSecondaryAbilityStaticChance(SubSkillType subSkillType)
  337. {
  338. switch(subSkillType)
  339. {
  340. case AXES_ARMOR_IMPACT:
  341. return AdvancedConfig.getInstance().getImpactChance();
  342. case AXES_GREATER_IMPACT:
  343. return AdvancedConfig.getInstance().getGreaterImpactChance();
  344. case TAMING_FAST_FOOD_SERVICE:
  345. return AdvancedConfig.getInstance().getFastFoodChance();
  346. default:
  347. return 100.0D;
  348. }
  349. }
  350. /**
  351. * Used to determine whether or not a sub-skill activates from random chance (using static values)
  352. * @param subSkillType The identifier for this specific sub-skill
  353. * @param player The owner of this sub-skill
  354. * @param activationChance This is the value that we roll against, 100 is normal, and 75 is for lucky perk
  355. * @param chance This is the static modifier for our random calculations
  356. * @return true if random chance was successful and the event wasn't cancelled
  357. */
  358. private static boolean performRandomSkillCheckStatic(SubSkillType subSkillType, Player player, int activationChance, double chance) {
  359. SubSkillWeightedActivationCheckEvent event = new SubSkillWeightedActivationCheckEvent(player, subSkillType, chance);
  360. mcMMO.p.getServer().getPluginManager().callEvent(event);
  361. return (event.getChance() * activationChance) > Misc.getRandom().nextInt(activationChance) && !event.isCancelled();
  362. }
  363. /* NEW VERSION */
  364. private static boolean performRandomSkillCheckStatic(AbstractSubSkill abstractSubSkill, Player player, int activationChance, double chance) {
  365. SubSkillWeightedActivationCheckEvent event = new SubSkillWeightedActivationCheckEvent(player, abstractSubSkill, chance);
  366. mcMMO.p.getServer().getPluginManager().callEvent(event);
  367. return (event.getChance() * activationChance) > Misc.getRandom().nextInt(activationChance) && !event.isCancelled();
  368. }
  369. public static boolean treasureDropSuccessful(Player player, double dropChance, int activationChance) {
  370. SubSkillWeightedActivationCheckEvent event = new SubSkillWeightedActivationCheckEvent(player, SubSkillType.EXCAVATION_TREASURE_HUNTER, dropChance / activationChance);
  371. mcMMO.p.getServer().getPluginManager().callEvent(event);
  372. return (event.getChance() * activationChance) > (Misc.getRandom().nextDouble() * activationChance) && !event.isCancelled();
  373. }
  374. private static boolean isLocalizedSkill(String skillName) {
  375. for (PrimarySkill skill : PrimarySkill.values()) {
  376. if (skillName.equalsIgnoreCase(LocaleLoader.getString(StringUtils.getCapitalized(skill.toString()) + ".SkillName"))) {
  377. return true;
  378. }
  379. }
  380. return false;
  381. }
  382. protected static Material getRepairAndSalvageItem(ItemStack inHand) {
  383. if (ItemUtils.isDiamondTool(inHand) || ItemUtils.isDiamondArmor(inHand)) {
  384. return Material.DIAMOND;
  385. }
  386. else if (ItemUtils.isGoldTool(inHand) || ItemUtils.isGoldArmor(inHand)) {
  387. return Material.GOLD_INGOT;
  388. }
  389. else if (ItemUtils.isIronTool(inHand) || ItemUtils.isIronArmor(inHand)) {
  390. return Material.IRON_INGOT;
  391. }
  392. else if (ItemUtils.isStoneTool(inHand)) {
  393. return Material.COBBLESTONE;
  394. }
  395. else if (ItemUtils.isWoodTool(inHand)) {
  396. return Material.OAK_WOOD;
  397. }
  398. else if (ItemUtils.isLeatherArmor(inHand)) {
  399. return Material.LEATHER;
  400. }
  401. else if (ItemUtils.isStringTool(inHand)) {
  402. return Material.STRING;
  403. }
  404. else {
  405. return null;
  406. }
  407. }
  408. public static int getRepairAndSalvageQuantities(ItemStack item) {
  409. return getRepairAndSalvageQuantities(item, getRepairAndSalvageItem(item), (byte) -1);
  410. }
  411. public static int getRepairAndSalvageQuantities(ItemStack item, Material repairMaterial, byte repairMetadata) {
  412. // Workaround for Bukkit bug where damaged items would not return any recipes
  413. item = item.clone();
  414. item.setDurability((short) 0);
  415. int quantity = 0;
  416. List<Recipe> recipes = mcMMO.p.getServer().getRecipesFor(item);
  417. if (recipes.isEmpty()) {
  418. return quantity;
  419. }
  420. Recipe recipe = recipes.get(0);
  421. if (recipe instanceof ShapelessRecipe) {
  422. for (ItemStack ingredient : ((ShapelessRecipe) recipe).getIngredientList()) {
  423. if (ingredient != null && (repairMaterial == null || ingredient.getType() == repairMaterial) && (repairMetadata == -1 || ingredient.getType().equals(repairMaterial))) {
  424. quantity += ingredient.getAmount();
  425. }
  426. }
  427. }
  428. else if (recipe instanceof ShapedRecipe) {
  429. for (ItemStack ingredient : ((ShapedRecipe) recipe).getIngredientMap().values()) {
  430. if (ingredient != null && (repairMaterial == null || ingredient.getType() == repairMaterial) && (repairMetadata == -1 || ingredient.getType().equals(repairMaterial))) {
  431. quantity += ingredient.getAmount();
  432. }
  433. }
  434. }
  435. return quantity;
  436. }
  437. }