ItemUtils.java 33 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892
  1. package com.gmail.nossr50.util;
  2. import com.gmail.nossr50.api.ItemSpawnReason;
  3. import com.gmail.nossr50.config.experience.ExperienceConfig;
  4. import com.gmail.nossr50.config.party.ItemWeightConfig;
  5. import com.gmail.nossr50.datatypes.treasure.EnchantmentWrapper;
  6. import com.gmail.nossr50.datatypes.treasure.FishingTreasureBook;
  7. import com.gmail.nossr50.events.items.McMMOItemSpawnEvent;
  8. import com.gmail.nossr50.locale.LocaleLoader;
  9. import com.gmail.nossr50.mcMMO;
  10. import com.gmail.nossr50.skills.smelting.Smelting;
  11. import org.bukkit.ChatColor;
  12. import org.bukkit.Location;
  13. import org.bukkit.Material;
  14. import org.bukkit.NamespacedKey;
  15. import org.bukkit.enchantments.Enchantment;
  16. import org.bukkit.entity.Item;
  17. import org.bukkit.entity.Player;
  18. import org.bukkit.inventory.FurnaceRecipe;
  19. import org.bukkit.inventory.ItemStack;
  20. import org.bukkit.inventory.Recipe;
  21. import org.bukkit.inventory.meta.EnchantmentStorageMeta;
  22. import org.bukkit.inventory.meta.ItemMeta;
  23. import org.bukkit.util.Vector;
  24. import org.jetbrains.annotations.NotNull;
  25. import org.jetbrains.annotations.Nullable;
  26. import java.lang.reflect.InvocationTargetException;
  27. import java.lang.reflect.Method;
  28. import java.util.Collection;
  29. import java.util.Collections;
  30. import java.util.List;
  31. import java.util.function.Predicate;
  32. import static java.util.Objects.requireNonNull;
  33. public final class ItemUtils {
  34. // Reflection for setItemName only available in newer APIs
  35. private static final Method setItemName;
  36. static {
  37. setItemName = getSetItemName();
  38. }
  39. private ItemUtils() {
  40. // private constructor
  41. }
  42. private static Method getSetItemName() {
  43. try {
  44. return ItemMeta.class.getMethod("setItemName", String.class);
  45. } catch (NoSuchMethodException e) {
  46. return null;
  47. }
  48. }
  49. /**
  50. * Sets the item name using the new API if available
  51. * or falls back to the old API.
  52. *
  53. * @param itemMeta The item meta to set the name on
  54. * @param name The name to set
  55. */
  56. public static void setItemName(ItemMeta itemMeta, String name) {
  57. if (setItemName != null) {
  58. setItemNameModern(itemMeta, name);
  59. } else {
  60. itemMeta.setDisplayName(ChatColor.RESET + name);
  61. }
  62. }
  63. private static void setItemNameModern(ItemMeta itemMeta, String name) {
  64. try {
  65. setItemName.invoke(itemMeta, name);
  66. } catch (IllegalAccessException | InvocationTargetException e) {
  67. mcMMO.p.getLogger().severe("Failed to set item name: " + e.getMessage());
  68. throw new RuntimeException(e);
  69. }
  70. }
  71. /**
  72. * Checks if the item is a bow.
  73. *
  74. * @param item Item to check
  75. * @return true if the item is a bow, false otherwise
  76. */
  77. // TODO: Unit tests
  78. public static boolean isBow(@NotNull ItemStack item) {
  79. return mcMMO.getMaterialMapStore().isBow(item.getType().getKey().getKey());
  80. }
  81. /**
  82. * Exhaustive lookup for a Material by name.
  83. * <p>
  84. * This method will first try a normal lookup, then a legacy lookup, then a lookup by ENUM name,
  85. * and finally a lookup by ENUM name with legacy name.
  86. *
  87. * @param materialName The name of the material to lookup
  88. * @return The Material if found, or null if not found
  89. */
  90. public static @Nullable Material exhaustiveMaterialLookup(@NotNull String materialName) {
  91. requireNonNull(materialName, "materialName cannot be null");
  92. // First try a normal lookup
  93. Material material = Material.matchMaterial(materialName);
  94. // If that fails, try a legacy lookup
  95. if (material == null) {
  96. material = Material.matchMaterial(materialName, true);
  97. }
  98. // try to match to Material ENUM
  99. if (material == null) {
  100. material = Material.getMaterial(materialName.toUpperCase());
  101. }
  102. // try to match to Material ENUM with legacy name
  103. if (material == null) {
  104. material = Material.getMaterial(materialName.toUpperCase(), true);
  105. }
  106. return material;
  107. }
  108. /**
  109. * Checks if a player has an item in their inventory or offhand.
  110. *
  111. * @param player Player to check
  112. * @param material Material to check for
  113. * @return true if the player has the item in their inventory or offhand, false otherwise
  114. */
  115. public static boolean hasItemIncludingOffHand(Player player, Material material) {
  116. // Checks main inventory / item bar
  117. boolean containsInMain = player.getInventory().contains(material);
  118. if (containsInMain) {
  119. return true;
  120. }
  121. return player.getInventory().getItemInOffHand().getType() == material;
  122. }
  123. /**
  124. * Removes an item from a player's inventory, including their offhand.
  125. *
  126. * @param player Player to remove the item from
  127. * @param material Material to remove
  128. * @param amount Amount of the material to remove
  129. */
  130. public static void removeItemIncludingOffHand(@NotNull Player player, @NotNull Material material, int amount) {
  131. // Checks main inventory / item bar
  132. if (player.getInventory().contains(material)) {
  133. player.getInventory().removeItem(new ItemStack(material, amount));
  134. return;
  135. }
  136. // Check off-hand
  137. final ItemStack offHandItem = player.getInventory().getItemInOffHand();
  138. if (offHandItem.getType() == material) {
  139. int newAmount = offHandItem.getAmount() - amount;
  140. if (newAmount > 0) {
  141. offHandItem.setAmount(newAmount);
  142. } else {
  143. player.getInventory().setItemInOffHand(new ItemStack(Material.AIR));
  144. }
  145. }
  146. }
  147. // TODO: Unit tests
  148. public static boolean isCrossbow(@NotNull ItemStack item) {
  149. return mcMMO.getMaterialMapStore().isCrossbow(item.getType().getKey().getKey());
  150. }
  151. // TODO: Unit tests
  152. public static boolean isTrident(@NotNull ItemStack item) {
  153. return mcMMO.getMaterialMapStore().isTrident(item.getType().getKey().getKey());
  154. }
  155. public static boolean isMace(@NotNull ItemStack item) {
  156. return mcMMO.getMaterialMapStore().isMace(item.getType().getKey().getKey());
  157. }
  158. public static boolean hasItemInEitherHand(@NotNull Player player, Material material) {
  159. return player.getInventory().getItemInMainHand().getType() == material
  160. || player.getInventory().getItemInOffHand().getType() == material;
  161. }
  162. public static boolean doesPlayerHaveEnchantmentOnArmor(@NotNull Player player, @NotNull String enchantmentByName) {
  163. Enchantment enchantment = getEnchantment(enchantmentByName);
  164. if (enchantment == null)
  165. return false;
  166. return doesPlayerHaveEnchantmentOnArmor(player, enchantment);
  167. }
  168. public static boolean doesPlayerHaveEnchantmentOnArmor(@NotNull Player player, @NotNull Enchantment enchantment) {
  169. for (ItemStack itemStack : player.getInventory().getArmorContents()) {
  170. if (itemStack != null) {
  171. if (hasEnchantment(itemStack, enchantment))
  172. return true;
  173. }
  174. }
  175. return false;
  176. }
  177. public static boolean doesPlayerHaveEnchantmentOnArmorOrHands(@NotNull Player player, @NotNull String enchantmentName) {
  178. Enchantment enchantment = getEnchantment(enchantmentName);
  179. if (enchantment == null)
  180. return false;
  181. return doesPlayerHaveEnchantmentOnArmorOrHands(player, enchantment);
  182. }
  183. public static boolean doesPlayerHaveEnchantmentOnArmorOrHands(@NotNull Player player, @NotNull Enchantment enchantment) {
  184. if (doesPlayerHaveEnchantmentOnArmor(player, enchantment))
  185. return true;
  186. if (doesPlayerHaveEnchantmentInHands(player, enchantment))
  187. return true;
  188. return false;
  189. }
  190. public static boolean doesPlayerHaveEnchantmentInHands(@NotNull Player player, @NotNull NamespacedKey enchantmentNameKey) {
  191. Enchantment enchantment = Enchantment.getByKey(enchantmentNameKey);
  192. if (enchantment == null)
  193. return false;
  194. return doesPlayerHaveEnchantmentInHands(player, enchantment);
  195. }
  196. public static boolean doesPlayerHaveEnchantmentInHands(@NotNull Player player, @NotNull String enchantmentName) {
  197. Enchantment enchantment = getEnchantment(enchantmentName);
  198. if (enchantment == null)
  199. return false;
  200. return doesPlayerHaveEnchantmentInHands(player, enchantment);
  201. }
  202. public static boolean doesPlayerHaveEnchantmentInHands(@NotNull Player player, @NotNull Enchantment enchantment) {
  203. return hasEnchantment(player.getInventory().getItemInMainHand(), enchantment) ||
  204. hasEnchantment(player.getInventory().getItemInOffHand(), enchantment);
  205. }
  206. public static boolean hasEnchantment(@NotNull ItemStack itemStack, @NotNull Enchantment enchantment) {
  207. if (itemStack.getItemMeta() != null) {
  208. return itemStack.getItemMeta().hasEnchant(enchantment);
  209. }
  210. return false;
  211. }
  212. public static @Nullable Enchantment getEnchantment(@NotNull String enchantmentName) {
  213. for (Enchantment enchantment : Enchantment.values()) {
  214. if (enchantment.getKey().getKey().equalsIgnoreCase(enchantmentName)) {
  215. return enchantment;
  216. }
  217. }
  218. return null;
  219. }
  220. /**
  221. * Checks if the item is a sword.
  222. *
  223. * @param item Item to check
  224. * @return true if the item is a sword, false otherwise
  225. */
  226. public static boolean isSword(@NotNull ItemStack item) {
  227. return mcMMO.getMaterialMapStore().isSword(item.getType().getKey().getKey());
  228. }
  229. /**
  230. * Checks if the item is a hoe.
  231. *
  232. * @param item Item to check
  233. * @return true if the item is a hoe, false otherwise
  234. */
  235. public static boolean isHoe(@NotNull ItemStack item) {
  236. return mcMMO.getMaterialMapStore().isHoe(item.getType().getKey().getKey());
  237. }
  238. /**
  239. * Checks if the item is a shovel.
  240. *
  241. * @param item Item to check
  242. * @return true if the item is a shovel, false otherwise
  243. */
  244. public static boolean isShovel(@NotNull ItemStack item) {
  245. return mcMMO.getMaterialMapStore().isShovel(item.getType().getKey().getKey());
  246. }
  247. /**
  248. * Checks if the item is an axe.
  249. *
  250. * @param item Item to check
  251. * @return true if the item is an axe, false otherwise
  252. */
  253. public static boolean isAxe(@NotNull ItemStack item) {
  254. return mcMMO.getMaterialMapStore().isAxe(item.getType().getKey().getKey());
  255. }
  256. /**
  257. * Checks if the item is a pickaxe.
  258. *
  259. * @param item Item to check
  260. * @return true if the item is a pickaxe, false otherwise
  261. */
  262. public static boolean isPickaxe(@NotNull ItemStack item) {
  263. return mcMMO.getMaterialMapStore().isPickAxe(item.getType().getKey().getKey());
  264. }
  265. /**
  266. * Checks if the item counts as unarmed.
  267. *
  268. * @param item Item to check
  269. * @return true if the item counts as unarmed, false otherwise
  270. */
  271. public static boolean isUnarmed(ItemStack item) {
  272. if (mcMMO.p.getGeneralConfig().getUnarmedItemsAsUnarmed()) {
  273. return !isMinecraftTool(item);
  274. }
  275. return item.getType() == Material.AIR;
  276. }
  277. /**
  278. * Checks to see if an item is a wearable armor piece.
  279. *
  280. * @param item Item to check
  281. * @return true if the item is armor, false otherwise
  282. */
  283. public static boolean isArmor(ItemStack item) {
  284. return mcMMO.getMaterialMapStore().isArmor(item.getType());
  285. }
  286. /**
  287. * Checks to see if an item is a leather armor piece.
  288. *
  289. * @param item Item to check
  290. * @return true if the item is leather armor, false otherwise
  291. */
  292. public static boolean isLeatherArmor(ItemStack item) {
  293. return mcMMO.getMaterialMapStore().isLeatherArmor(item.getType());
  294. }
  295. /**
  296. * Checks to see if an item is a gold armor piece.
  297. *
  298. * @param item Item to check
  299. * @return true if the item is gold armor, false otherwise
  300. */
  301. public static boolean isGoldArmor(ItemStack item) {
  302. return mcMMO.getMaterialMapStore().isGoldArmor(item.getType().getKey().getKey());
  303. }
  304. /**
  305. * Checks to see if an item is an iron armor piece.
  306. *
  307. * @param item Item to check
  308. * @return true if the item is iron armor, false otherwise
  309. */
  310. public static boolean isIronArmor(ItemStack item) {
  311. return mcMMO.getMaterialMapStore().isIronArmor(item.getType().getKey().getKey());
  312. }
  313. /**
  314. * Checks to see if an item is a diamond armor piece.
  315. *
  316. * @param item Item to check
  317. * @return true if the item is diamond armor, false otherwise
  318. */
  319. public static boolean isDiamondArmor(ItemStack item) {
  320. return mcMMO.getMaterialMapStore().isDiamondArmor(item.getType().getKey().getKey());
  321. }
  322. public static boolean isNetheriteArmor(ItemStack itemStack) {
  323. return mcMMO.getMaterialMapStore().isNetheriteArmor(itemStack.getType().getKey().getKey());
  324. }
  325. public static boolean isNetheriteTool(ItemStack itemStack) {
  326. return mcMMO.getMaterialMapStore().isNetheriteTool(itemStack.getType().getKey().getKey());
  327. }
  328. /**
  329. * Checks to see if an item is a chainmail armor piece.
  330. *
  331. * @param item Item to check
  332. * @return true if the item is chainmail armor, false otherwise
  333. */
  334. public static boolean isChainmailArmor(ItemStack item) {
  335. return mcMMO.getMaterialMapStore().isChainmailArmor(item.getType().getKey().getKey());
  336. }
  337. /**
  338. * Checks to see if an item is a *vanilla* tool.
  339. *
  340. * @param item Item to check
  341. * @return true if the item is a tool, false otherwise
  342. */
  343. public static boolean isMinecraftTool(ItemStack item) {
  344. return mcMMO.getMaterialMapStore().isTool(item.getType().getKey().getKey());
  345. }
  346. /**
  347. * Checks to see if an item is a stone tool.
  348. *
  349. * @param item Item to check
  350. * @return true if the item is a stone tool, false otherwise
  351. */
  352. public static boolean isStoneTool(ItemStack item) {
  353. return mcMMO.getMaterialMapStore().isStoneTool(item.getType().getKey().getKey());
  354. }
  355. /**
  356. * Checks to see if an item is a wooden tool.
  357. *
  358. * @param item Item to check
  359. * @return true if the item is a wooden tool, false otherwise
  360. */
  361. public static boolean isWoodTool(ItemStack item) {
  362. return mcMMO.getMaterialMapStore().isWoodTool(item.getType().getKey().getKey());
  363. }
  364. /**
  365. * Checks to see if an item is a string tool.
  366. *
  367. * @param item Item to check
  368. * @return true if the item is a string tool, false otherwise
  369. */
  370. public static boolean isStringTool(ItemStack item) {
  371. return mcMMO.getMaterialMapStore().isStringTool(item.getType().getKey().getKey());
  372. }
  373. /**
  374. * Checks to see if an item is a gold tool.
  375. *
  376. * @param item Item to check
  377. * @return true if the item is a stone tool, false otherwise
  378. */
  379. public static boolean isGoldTool(ItemStack item) {
  380. return mcMMO.getMaterialMapStore().isGoldTool(item.getType().getKey().getKey());
  381. }
  382. /**
  383. * Checks to see if an item is an iron tool.
  384. *
  385. * @param item Item to check
  386. * @return true if the item is an iron tool, false otherwise
  387. */
  388. public static boolean isIronTool(ItemStack item) {
  389. return mcMMO.getMaterialMapStore().isIronTool(item.getType().getKey().getKey());
  390. }
  391. /**
  392. * Checks to see if an item is a diamond tool.
  393. *
  394. * @param item Item to check
  395. * @return true if the item is a diamond tool, false otherwise
  396. */
  397. public static boolean isDiamondTool(ItemStack item) {
  398. return mcMMO.getMaterialMapStore().isDiamondTool(item.getType().getKey().getKey());
  399. }
  400. /**
  401. * Checks to see if an item is enchantable.
  402. *
  403. * @param item Item to check
  404. * @return true if the item is enchantable, false otherwise
  405. */
  406. public static boolean isEnchantable(ItemStack item) {
  407. return mcMMO.getMaterialMapStore().isEnchantable(item.getType().getKey().getKey());
  408. }
  409. public static boolean isSmeltable(ItemStack item) {
  410. return item != null && Smelting.getSmeltXP(item) >= 1;
  411. }
  412. public static boolean isSmelted(ItemStack item) {
  413. if (item == null) {
  414. return false;
  415. }
  416. for (Recipe recipe : mcMMO.p.getServer().getRecipesFor(item)) {
  417. if (recipe instanceof FurnaceRecipe
  418. && ((FurnaceRecipe) recipe).getInput().getType().isBlock()
  419. && MaterialUtils.isOre(((FurnaceRecipe) recipe).getInput().getType())) {
  420. return true;
  421. }
  422. }
  423. return false;
  424. }
  425. /**
  426. * Check if an item is sharable.
  427. *
  428. * @param item Item that will get shared
  429. * @return True if the item can be shared.
  430. */
  431. public static boolean isSharable(ItemStack item) {
  432. if (item == null || item.getType() == Material.AIR) {
  433. return false;
  434. }
  435. return isMiningDrop(item)
  436. || isWoodcuttingDrop(item)
  437. || isMobDrop(item)
  438. || isHerbalismDrop(item)
  439. || isMiscDrop(item);
  440. }
  441. /**
  442. * Checks to see if an item is a mining drop.
  443. *
  444. * @param item Item to check
  445. * @return true if the item is a mining drop, false otherwise
  446. */
  447. public static boolean isMiningDrop(ItemStack item) {
  448. //TODO: 1.14 This needs to be updated
  449. return switch (item.getType()) { // Should we also have Glowing Redstone Ore here?
  450. // Should we also have Glowstone here?
  451. case COAL, COAL_ORE, DIAMOND, DIAMOND_ORE, EMERALD, EMERALD_ORE, GOLD_ORE, IRON_ORE, LAPIS_ORE,
  452. REDSTONE_ORE, REDSTONE, GLOWSTONE_DUST, QUARTZ, NETHER_QUARTZ_ORE, LAPIS_LAZULI -> true;
  453. default -> false;
  454. };
  455. }
  456. /**
  457. * Checks to see if an item is a herbalism drop.
  458. *
  459. * @param item Item to check
  460. * @return true if the item is a herbalism drop, false otherwise
  461. */
  462. public static boolean isHerbalismDrop(ItemStack item) {
  463. //TODO: 1.14 This needs to be updated
  464. return switch (item.getType().getKey().getKey().toLowerCase()) {
  465. case "wheat", "wheat_seeds", "carrot", "chorus_fruit", "chorus_flower", "potato", "beetroot", "beetroots",
  466. "beetroot_seeds", "nether_wart", "brown_mushroom", "red_mushroom", "rose_bush", "dandelion", "cactus",
  467. "sugar_cane", "melon", "melon_seeds", "pumpkin", "pumpkin_seeds", "lily_pad", "vine", "tall_grass",
  468. "cocoa_beans" -> true;
  469. default -> false;
  470. };
  471. }
  472. /**
  473. * Checks to see if an item is a mob drop.
  474. *
  475. * @param item Item to check
  476. * @return true if the item is a mob drop, false otherwise
  477. */
  478. public static boolean isMobDrop(ItemStack item) {
  479. //TODO: 1.14 This needs to be updated
  480. return switch (item.getType()) {
  481. case STRING, FEATHER, CHICKEN, COOKED_CHICKEN, LEATHER, BEEF, COOKED_BEEF, PORKCHOP, COOKED_PORKCHOP,
  482. WHITE_WOOL, BLACK_WOOL, BLUE_WOOL, BROWN_WOOL, CYAN_WOOL, GRAY_WOOL, GREEN_WOOL, LIGHT_BLUE_WOOL,
  483. LIGHT_GRAY_WOOL, LIME_WOOL, MAGENTA_WOOL, ORANGE_WOOL, PINK_WOOL, PURPLE_WOOL, RED_WOOL, YELLOW_WOOL,
  484. IRON_INGOT, SNOWBALL, BLAZE_ROD, SPIDER_EYE, GUNPOWDER, ENDER_PEARL, GHAST_TEAR, MAGMA_CREAM, BONE,
  485. ARROW, SLIME_BALL, NETHER_STAR, ROTTEN_FLESH, GOLD_NUGGET, EGG, ROSE_BUSH, COAL -> true;
  486. default -> false;
  487. };
  488. }
  489. /**
  490. * Checks to see if an item is a woodcutting drop.
  491. *
  492. * @param item Item to check
  493. * @return true if the item is a woodcutting drop, false otherwise
  494. */
  495. public static boolean isWoodcuttingDrop(ItemStack item) {
  496. return switch (item.getType().toString()) {
  497. case "ACACIA_LOG", "BIRCH_LOG", "DARK_OAK_LOG", "JUNGLE_LOG", "OAK_LOG", "SPRUCE_LOG",
  498. "STRIPPED_ACACIA_LOG", "STRIPPED_BIRCH_LOG", "STRIPPED_DARK_OAK_LOG", "STRIPPED_JUNGLE_LOG",
  499. "STRIPPED_OAK_LOG", "STRIPPED_SPRUCE_LOG", "STRIPPED_MANGROVE_LOG", "ACACIA_SAPLING", "SPRUCE_SAPLING",
  500. "BIRCH_SAPLING", "DARK_OAK_SAPLING", "JUNGLE_SAPLING", "OAK_SAPLING", "ACACIA_LEAVES", "BIRCH_LEAVES",
  501. "DARK_OAK_LEAVES", "JUNGLE_LEAVES", "OAK_LEAVES", "SPRUCE_LEAVES", "BEE_NEST", "APPLE" -> true;
  502. default -> false;
  503. };
  504. }
  505. /**
  506. * Checks to see if an item is a miscellaneous drop. These items are read from the config file
  507. *
  508. * @param item Item to check
  509. * @return true if the item is a miscellaneous drop, false otherwise
  510. */
  511. public static boolean isMiscDrop(ItemStack item) {
  512. return ItemWeightConfig.getInstance().getMiscItems().contains(item.getType());
  513. }
  514. public static boolean isMcMMOItem(ItemStack item) {
  515. if (!item.hasItemMeta()) {
  516. return false;
  517. }
  518. ItemMeta itemMeta = item.getItemMeta();
  519. if (itemMeta == null)
  520. return false;
  521. return itemMeta.getLore() != null
  522. && itemMeta.getLore().contains("mcMMO Item");
  523. }
  524. public static boolean isChimaeraWing(ItemStack item) {
  525. if (!isMcMMOItem(item)) {
  526. return false;
  527. }
  528. ItemMeta itemMeta = item.getItemMeta();
  529. if (itemMeta == null)
  530. return false;
  531. return itemMeta.hasDisplayName() && itemMeta.getDisplayName().equals(ChatColor.GOLD + LocaleLoader.getString("Item.ChimaeraWing.Name"));
  532. }
  533. public static void removeAbilityLore(@NotNull ItemStack itemStack) {
  534. ItemMeta itemMeta = itemStack.getItemMeta();
  535. if (itemMeta == null)
  536. return;
  537. if (itemMeta.hasLore()) {
  538. List<String> itemLore = itemMeta.getLore();
  539. if (itemLore == null)
  540. return;
  541. if (itemLore.remove("mcMMO Ability Tool")) {
  542. itemMeta.setLore(itemLore);
  543. itemStack.setItemMeta(itemMeta);
  544. }
  545. }
  546. }
  547. public static void addDigSpeedToItem(@NotNull ItemStack itemStack,
  548. int existingEnchantLevel) {
  549. ItemMeta itemMeta = itemStack.getItemMeta();
  550. if (itemMeta == null)
  551. return;
  552. itemMeta.addEnchant(mcMMO.p.getEnchantmentMapper().getEfficiency(),
  553. existingEnchantLevel + mcMMO.p.getAdvancedConfig().getEnchantBuff(), true);
  554. itemStack.setItemMeta(itemMeta);
  555. }
  556. public static boolean canBeSuperAbilityDigBoosted(@NotNull ItemStack itemStack) {
  557. return isShovel(itemStack) || isPickaxe(itemStack);
  558. }
  559. public static @NotNull ItemStack createEnchantBook(@NotNull FishingTreasureBook fishingTreasureBook) {
  560. ItemStack itemStack = fishingTreasureBook.getDrop().clone();
  561. EnchantmentWrapper enchantmentWrapper = getRandomEnchantment(fishingTreasureBook.getLegalEnchantments());
  562. ItemMeta itemMeta = itemStack.getItemMeta();
  563. if (itemMeta == null) {
  564. return itemStack;
  565. }
  566. EnchantmentStorageMeta enchantmentStorageMeta = (EnchantmentStorageMeta) itemMeta;
  567. enchantmentStorageMeta.addStoredEnchant(
  568. enchantmentWrapper.getEnchantment(),
  569. enchantmentWrapper.getEnchantmentLevel(),
  570. ExperienceConfig.getInstance().allowUnsafeEnchantments());
  571. itemStack.setItemMeta(enchantmentStorageMeta);
  572. return itemStack;
  573. }
  574. public static @NotNull EnchantmentWrapper getRandomEnchantment(
  575. @NotNull List<EnchantmentWrapper> enchantmentWrappers) {
  576. Collections.shuffle(enchantmentWrappers, Misc.getRandom());
  577. int randomIndex = Misc.getRandom().nextInt(enchantmentWrappers.size());
  578. return enchantmentWrappers.get(randomIndex);
  579. }
  580. /**
  581. * Drop items at a given location.
  582. *
  583. * @param location The location to drop the items at
  584. * @param itemStacks The items to drop
  585. */
  586. public static void spawnItems(@Nullable Player player,
  587. @NotNull Location location,
  588. @NotNull Collection<ItemStack> itemStacks,
  589. @NotNull ItemSpawnReason itemSpawnReason) {
  590. for (ItemStack is : itemStacks) {
  591. spawnItem(player, location, is, itemSpawnReason);
  592. }
  593. }
  594. /**
  595. * Drop items at a given location.
  596. *
  597. * @param location The location to drop the items at
  598. * @param is The items to drop
  599. * @param quantity The amount of items to drop
  600. */
  601. public static void spawnItems(@Nullable Player player,
  602. @NotNull Location location,
  603. @NotNull ItemStack is,
  604. int quantity,
  605. @NotNull ItemSpawnReason itemSpawnReason) {
  606. for (int i = 0; i < quantity; i++) {
  607. spawnItem(player, location, is, itemSpawnReason);
  608. }
  609. }
  610. /**
  611. * Drop an item at a given location.
  612. *
  613. * @param location The location to drop the item at
  614. * @param itemStack The item to drop
  615. * @param itemSpawnReason the reason for the item drop
  616. * @return Dropped Item entity or null if invalid or cancelled
  617. */
  618. public static @Nullable Item spawnItem(@Nullable Player player,
  619. @NotNull Location location,
  620. @NotNull ItemStack itemStack,
  621. @NotNull ItemSpawnReason itemSpawnReason) {
  622. if (itemStack.getType() == Material.AIR || location.getWorld() == null) {
  623. return null;
  624. }
  625. // We can't get the item until we spawn it and we want to make it cancellable, so we have a custom event.
  626. McMMOItemSpawnEvent event = new McMMOItemSpawnEvent(location, itemStack, itemSpawnReason, player);
  627. mcMMO.p.getServer().getPluginManager().callEvent(event);
  628. if (event.isCancelled()) {
  629. return null;
  630. }
  631. return location.getWorld().dropItem(location, itemStack);
  632. }
  633. /**
  634. * Drop an item at a given location.
  635. *
  636. * @param location The location to drop the item at
  637. * @param itemStack The item to drop
  638. * @param itemSpawnReason the reason for the item drop
  639. * @return Dropped Item entity or null if invalid or cancelled
  640. */
  641. public static @Nullable Item spawnItemNaturally(@Nullable Player player,
  642. @NotNull Location location,
  643. @NotNull ItemStack itemStack,
  644. @NotNull ItemSpawnReason itemSpawnReason) {
  645. if (itemStack.getType() == Material.AIR || location.getWorld() == null) {
  646. return null;
  647. }
  648. // We can't get the item until we spawn it and we want to make it cancellable, so we have a custom event.
  649. McMMOItemSpawnEvent event = new McMMOItemSpawnEvent(location, itemStack, itemSpawnReason, player);
  650. mcMMO.p.getServer().getPluginManager().callEvent(event);
  651. if (event.isCancelled()) {
  652. return null;
  653. }
  654. return location.getWorld().dropItemNaturally(location, itemStack);
  655. }
  656. /**
  657. * Drop items at a given location.
  658. *
  659. * @param fromLocation The location to drop the items at
  660. * @param is The items to drop
  661. * @param speed the speed that the item should travel
  662. * @param quantity The amount of items to drop
  663. */
  664. public static void spawnItemsTowardsLocation(@Nullable Player player,
  665. @NotNull Location fromLocation,
  666. @NotNull Location toLocation,
  667. @NotNull ItemStack is,
  668. int quantity,
  669. double speed,
  670. @NotNull ItemSpawnReason itemSpawnReason) {
  671. for (int i = 0; i < quantity; i++) {
  672. spawnItemTowardsLocation(player, fromLocation, toLocation, is, speed, itemSpawnReason);
  673. }
  674. }
  675. /**
  676. * Drop an item at a given location.
  677. * This method is fairly expensive as it creates clones of everything passed to itself since they are mutable objects
  678. *
  679. * @param fromLocation The location to drop the item at
  680. * @param toLocation The location the item will travel towards
  681. * @param itemToSpawn The item to spawn
  682. * @param speed the speed that the item should travel
  683. * @return Dropped Item entity or null if invalid or cancelled
  684. */
  685. public static @Nullable Item spawnItemTowardsLocation(@Nullable Player player,
  686. @NotNull Location fromLocation,
  687. @NotNull Location toLocation,
  688. @NotNull ItemStack itemToSpawn,
  689. double speed,
  690. @NotNull ItemSpawnReason itemSpawnReason) {
  691. if (itemToSpawn.getType() == Material.AIR) {
  692. return null;
  693. }
  694. //Work with fresh copies of everything
  695. ItemStack clonedItem = itemToSpawn.clone();
  696. Location spawnLocation = fromLocation.clone();
  697. Location targetLocation = toLocation.clone();
  698. if (spawnLocation.getWorld() == null)
  699. return null;
  700. // We can't get the item until we spawn it and we want to make it cancellable, so we have a custom event.
  701. McMMOItemSpawnEvent event = new McMMOItemSpawnEvent(spawnLocation, clonedItem, itemSpawnReason, player);
  702. mcMMO.p.getServer().getPluginManager().callEvent(event);
  703. //Something cancelled the event so back out
  704. if (event.isCancelled()) {
  705. return null;
  706. }
  707. //Use the item from the event
  708. Item spawnedItem = spawnLocation.getWorld().dropItem(spawnLocation, clonedItem);
  709. Vector vecFrom = spawnLocation.clone().toVector().clone();
  710. Vector vecTo = targetLocation.clone().toVector().clone();
  711. //Vector which is pointing towards out target location
  712. Vector direction = vecTo.subtract(vecFrom).normalize();
  713. //Modify the speed of the vector
  714. direction = direction.multiply(speed);
  715. spawnedItem.setVelocity(direction);
  716. return spawnedItem;
  717. }
  718. public static void spawnItemsFromCollection(@NotNull Player player,
  719. @NotNull Location location,
  720. @NotNull Collection<ItemStack> drops,
  721. @NotNull ItemSpawnReason itemSpawnReason) {
  722. requireNonNull(drops, "drops cannot be null");
  723. for (ItemStack drop : drops) {
  724. spawnItem(player, location, drop, itemSpawnReason);
  725. }
  726. }
  727. /**
  728. * Drops only the first n items in a collection
  729. * Size should always be a positive integer above 0
  730. *
  731. * @param location target drop location
  732. * @param drops collection to iterate over
  733. * @param sizeLimit the number of drops to process
  734. */
  735. public static void spawnItemsFromCollection(@Nullable Player player,
  736. @NotNull Location location,
  737. @NotNull Collection<ItemStack> drops,
  738. @NotNull ItemSpawnReason itemSpawnReason,
  739. int sizeLimit) {
  740. // TODO: This doesn't make much sense, unit test time?
  741. final ItemStack[] arrayDrops = drops.toArray(new ItemStack[0]);
  742. for (int i = 0; i < sizeLimit - 1; i++) {
  743. spawnItem(player, location, arrayDrops[i], itemSpawnReason);
  744. }
  745. }
  746. /**
  747. * Spawn items form a collection if conditions are met.
  748. * Each item is tested against the condition and spawned if it passes.
  749. *
  750. * @param potentialItemDrops The collection of items to iterate over, each one is tested and spawned if the
  751. * predicate is true
  752. * @param predicate The predicate to test the item against
  753. * @param itemSpawnReason The reason for the item drop
  754. * @param spawnLocation The location to spawn the item at
  755. * @param player The player to spawn the item for
  756. */
  757. public static void spawnItemsConditionally(@NotNull Collection<ItemStack> potentialItemDrops,
  758. @NotNull Predicate<ItemStack> predicate,
  759. @NotNull ItemSpawnReason itemSpawnReason,
  760. @NotNull Location spawnLocation,
  761. @NotNull Player player) {
  762. potentialItemDrops.stream()
  763. .filter(predicate)
  764. .forEach(itemStack -> spawnItem(player, spawnLocation, itemStack, itemSpawnReason));
  765. }
  766. }