FishingTreasureConfig.java 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403
  1. package com.gmail.nossr50.config.treasure;
  2. import com.gmail.nossr50.config.BukkitConfig;
  3. import com.gmail.nossr50.datatypes.treasure.*;
  4. import com.gmail.nossr50.mcMMO;
  5. import com.gmail.nossr50.util.EnchantmentUtils;
  6. import com.gmail.nossr50.util.LogUtils;
  7. import com.gmail.nossr50.util.PotionUtil;
  8. import org.bukkit.ChatColor;
  9. import org.bukkit.Material;
  10. import org.bukkit.configuration.ConfigurationSection;
  11. import org.bukkit.enchantments.Enchantment;
  12. import org.bukkit.entity.EntityType;
  13. import org.bukkit.inventory.ItemStack;
  14. import org.bukkit.inventory.meta.ItemMeta;
  15. import org.bukkit.inventory.meta.PotionMeta;
  16. import org.bukkit.potion.PotionType;
  17. import org.jetbrains.annotations.NotNull;
  18. import java.util.*;
  19. import static com.gmail.nossr50.util.PotionUtil.matchPotionType;
  20. public class FishingTreasureConfig extends BukkitConfig {
  21. public static final String FILENAME = "fishing_treasures.yml";
  22. private static FishingTreasureConfig instance;
  23. public @NotNull HashMap<Rarity, List<FishingTreasure>> fishingRewards = new HashMap<>();
  24. public @NotNull HashMap<Rarity, List<EnchantmentTreasure>> fishingEnchantments = new HashMap<>();
  25. public @NotNull HashMap<EntityType, List<ShakeTreasure>> shakeMap = new HashMap<>();
  26. private FishingTreasureConfig() {
  27. super(FILENAME, false);
  28. loadKeys();
  29. validate();
  30. }
  31. public static FishingTreasureConfig getInstance() {
  32. if (instance == null) {
  33. instance = new FishingTreasureConfig();
  34. }
  35. return instance;
  36. }
  37. @Override
  38. protected boolean validateKeys() {
  39. // Validate all the settings!
  40. List<String> reason = new ArrayList<>();
  41. ConfigurationSection enchantment_drop_rates = config.getConfigurationSection("Enchantment_Drop_Rates");
  42. if(enchantment_drop_rates != null) {
  43. for (String tier : enchantment_drop_rates.getKeys(false)) {
  44. double totalEnchantDropRate = 0;
  45. double totalItemDropRate = 0;
  46. for (Rarity rarity : Rarity.values()) {
  47. double enchantDropRate = config.getDouble("Enchantment_Drop_Rates." + tier + "." + rarity.toString());
  48. double itemDropRate = config.getDouble("Item_Drop_Rates." + tier + "." + rarity);
  49. if ((enchantDropRate < 0.0 || enchantDropRate > 100.0)) {
  50. reason.add("The enchant drop rate for " + tier + " items that are " + rarity + "should be between 0.0 and 100.0!");
  51. }
  52. if (itemDropRate < 0.0 || itemDropRate > 100.0) {
  53. reason.add("The item drop rate for " + tier + " items that are " + rarity + "should be between 0.0 and 100.0!");
  54. }
  55. totalEnchantDropRate += enchantDropRate;
  56. totalItemDropRate += itemDropRate;
  57. }
  58. if (totalEnchantDropRate < 0 || totalEnchantDropRate > 100.0) {
  59. reason.add("The total enchant drop rate for " + tier + " should be between 0.0 and 100.0!");
  60. }
  61. if (totalItemDropRate < 0 || totalItemDropRate > 100.0) {
  62. reason.add("The total item drop rate for " + tier + " should be between 0.0 and 100.0!");
  63. }
  64. }
  65. } else {
  66. mcMMO.p.getLogger().warning("Your fishing treasures config is empty, is this intentional? Delete it to regenerate.");
  67. }
  68. return noErrorsInConfig(reason);
  69. }
  70. @Override
  71. protected void loadKeys() {
  72. if (config.getConfigurationSection("Treasures") != null) {
  73. backup();
  74. return;
  75. }
  76. loadTreasures("Fishing");
  77. loadEnchantments();
  78. for (EntityType entity : EntityType.values()) {
  79. if (entity.isAlive()) {
  80. loadTreasures("Shake." + entity);
  81. }
  82. }
  83. }
  84. private void loadTreasures(@NotNull String type) {
  85. boolean isFishing = type.equals("Fishing");
  86. boolean isShake = type.contains("Shake");
  87. ConfigurationSection treasureSection = config.getConfigurationSection(type);
  88. if (treasureSection == null) {
  89. return;
  90. }
  91. // Initialize fishing HashMap
  92. for (Rarity rarity : Rarity.values()) {
  93. if (!fishingRewards.containsKey(rarity)) {
  94. fishingRewards.put(rarity, (new ArrayList<>()));
  95. }
  96. }
  97. for (String treasureName : treasureSection.getKeys(false)) {
  98. // Validate all the things!
  99. List<String> reason = new ArrayList<>();
  100. String[] treasureInfo = treasureName.split("[|]");
  101. String materialName = treasureInfo[0];
  102. /*
  103. * Material, Amount, and Data
  104. */
  105. Material material;
  106. if (materialName.contains("INVENTORY")) {
  107. // Use magic material BEDROCK to know that we're grabbing something from the inventory and not a normal treasure
  108. addShakeTreasure(new ShakeTreasure(new ItemStack(Material.BEDROCK, 1, (byte) 0), 1, getInventoryStealDropChance(), getInventoryStealDropLevel()), EntityType.PLAYER);
  109. continue;
  110. } else {
  111. material = Material.matchMaterial(materialName);
  112. }
  113. int amount = config.getInt(type + "." + treasureName + ".Amount");
  114. short data = (treasureInfo.length == 2) ? Short.parseShort(treasureInfo[1]) : (short) config.getInt(type + "." + treasureName + ".Data");
  115. if (material == null) {
  116. reason.add("Cannot find matching item type in this version of MC, skipping - " + materialName);
  117. continue;
  118. }
  119. if (amount <= 0) {
  120. amount = 1;
  121. }
  122. if (material.isBlock() && (data > 127 || data < -128)) {
  123. reason.add("Data of " + treasureName + " is invalid! " + data);
  124. }
  125. /*
  126. * XP, Drop Chance, and Drop Level
  127. */
  128. int xp = config.getInt(type + "." + treasureName + ".XP");
  129. double dropChance = config.getDouble(type + "." + treasureName + ".Drop_Chance");
  130. int dropLevel = config.getInt(type + "." + treasureName + ".Drop_Level");
  131. if (xp < 0) {
  132. reason.add(treasureName + " has an invalid XP value: " + xp);
  133. }
  134. if (dropChance < 0.0D) {
  135. reason.add(treasureName + " has an invalid Drop_Chance: " + dropChance);
  136. }
  137. if (dropLevel < 0) {
  138. reason.add("Fishing Config: " + treasureName + " has an invalid Drop_Level: " + dropLevel);
  139. }
  140. /*
  141. * Specific Types
  142. */
  143. Rarity rarity = null;
  144. if (isFishing) {
  145. String rarityStr = config.getString(type + "." + treasureName + ".Rarity");
  146. if (rarityStr != null) {
  147. rarity = Rarity.getRarity(rarityStr);
  148. } else {
  149. mcMMO.p.getLogger().severe("Please edit your config and add a Rarity definition for - " + treasureName);
  150. mcMMO.p.getLogger().severe("Skipping this treasure until rarity is defined - " + treasureName);
  151. continue;
  152. }
  153. }
  154. /*
  155. * Itemstack
  156. */
  157. ItemStack item = null;
  158. String customName = null;
  159. if (hasCustomName(type, treasureName)) {
  160. customName = config.getString(type + "." + treasureName + ".Custom_Name");
  161. }
  162. if (materialName.contains("POTION")) {
  163. // Update for 1.20.5
  164. Material mat = Material.matchMaterial(materialName);
  165. if (mat == null) {
  166. reason.add("Potion format for " + FILENAME + " has changed");
  167. continue;
  168. } else {
  169. item = new ItemStack(mat, amount, data);
  170. PotionMeta potionMeta = (PotionMeta) item.getItemMeta();
  171. if (potionMeta == null) {
  172. mcMMO.p.getLogger().severe("FishingConfig: Item meta when adding potion to fishing treasure was null," +
  173. " contact the mcMMO devs!");
  174. reason.add("FishingConfig: Item meta when adding potion to fishing treasure was null");
  175. continue;
  176. }
  177. String potionTypeStr;
  178. potionTypeStr = config.getString(type + "." + treasureName + ".PotionData.PotionType", "WATER");
  179. boolean extended = config.getBoolean(type + "." + treasureName + ".PotionData.Extended", false);
  180. boolean upgraded = config.getBoolean(type + "." + treasureName + ".PotionData.Upgraded", false);
  181. final PotionType potionType = matchPotionType(potionTypeStr, extended, upgraded);
  182. if (potionType == null) {
  183. reason.add("FishingConfig: Could not derive potion type from: " + potionTypeStr +", " + extended + ", " + upgraded);
  184. continue;
  185. }
  186. // Set the base potion type
  187. // NOTE: Upgraded/Extended are ignored in 1.20.5 and later
  188. PotionUtil.setBasePotionType(potionMeta, potionType, upgraded, extended);
  189. if (customName != null) {
  190. potionMeta.setDisplayName(ChatColor.translateAlternateColorCodes('&', customName));
  191. }
  192. if (config.contains(type + "." + treasureName + ".Lore")) {
  193. List<String> lore = new ArrayList<>();
  194. for (String s : config.getStringList(type + "." + treasureName + ".Lore")) {
  195. lore.add(ChatColor.translateAlternateColorCodes('&', s));
  196. }
  197. potionMeta.setLore(lore);
  198. }
  199. item.setItemMeta(potionMeta);
  200. }
  201. } else if (material == Material.ENCHANTED_BOOK) {
  202. //If any whitelisted enchants exist we use whitelist-based matching
  203. item = new ItemStack(material, 1);
  204. ItemMeta itemMeta = item.getItemMeta();
  205. List<String> allowedEnchantsList = config.getStringList(type + "." + treasureName + ".Enchantments_Whitelist");
  206. List<String> disallowedEnchantsList = config.getStringList(type + "." + treasureName + ".Enchantments_Blacklist");
  207. Set<Enchantment> blackListedEnchants = new HashSet<>();
  208. Set<Enchantment> whiteListedEnchants = new HashSet<>();
  209. matchAndFillSet(disallowedEnchantsList, blackListedEnchants);
  210. matchAndFillSet(allowedEnchantsList, whiteListedEnchants);
  211. if (customName != null && itemMeta != null) {
  212. itemMeta.setDisplayName(ChatColor.translateAlternateColorCodes('&', customName));
  213. item.setItemMeta(itemMeta);
  214. }
  215. FishingTreasureBook fishingTreasureBook = new FishingTreasureBook(item, xp, blackListedEnchants, whiteListedEnchants);
  216. addFishingTreasure(rarity, fishingTreasureBook);
  217. //TODO: Add book support for shake
  218. continue; //The code in this whole file is a disaster, ignore this hacky solution :P
  219. } else {
  220. item = new ItemStack(material, amount, data);
  221. if (customName != null) {
  222. ItemMeta itemMeta = item.getItemMeta();
  223. itemMeta.setDisplayName(ChatColor.translateAlternateColorCodes('&', customName));
  224. item.setItemMeta(itemMeta);
  225. }
  226. if (config.contains(type + "." + treasureName + ".Lore")) {
  227. ItemMeta itemMeta = item.getItemMeta();
  228. List<String> lore = new ArrayList<>();
  229. for (String s : config.getStringList(type + "." + treasureName + ".Lore")) {
  230. lore.add(ChatColor.translateAlternateColorCodes('&', s));
  231. }
  232. itemMeta.setLore(lore);
  233. item.setItemMeta(itemMeta);
  234. }
  235. }
  236. if (noErrorsInConfig(reason)) {
  237. if (isFishing) {
  238. addFishingTreasure(rarity, new FishingTreasure(item, xp));
  239. } else if (isShake) {
  240. ShakeTreasure shakeTreasure = new ShakeTreasure(item, xp, dropChance, dropLevel);
  241. EntityType entityType = EntityType.valueOf(type.substring(6));
  242. addShakeTreasure(shakeTreasure, entityType);
  243. }
  244. }
  245. }
  246. }
  247. private void addShakeTreasure(@NotNull ShakeTreasure shakeTreasure, @NotNull EntityType entityType) {
  248. if (!shakeMap.containsKey(entityType))
  249. shakeMap.put(entityType, new ArrayList<>());
  250. shakeMap.get(entityType).add(shakeTreasure);
  251. }
  252. private void addFishingTreasure(@NotNull Rarity rarity, @NotNull FishingTreasure fishingTreasure) {
  253. fishingRewards.get(rarity).add(fishingTreasure);
  254. }
  255. private boolean hasCustomName(@NotNull String type, @NotNull String treasureName) {
  256. return config.contains(type + "." + treasureName + ".Custom_Name");
  257. }
  258. /**
  259. * Matches enchantments on a list (user provided string) to known enchantments in the Spigot API
  260. * Any matches are added to the passed set
  261. *
  262. * @param enchantListStr the users string list of enchantments
  263. * @param permissiveList the permissive list of enchantments
  264. */
  265. private void matchAndFillSet(@NotNull List<String> enchantListStr, @NotNull Set<Enchantment> permissiveList) {
  266. if (enchantListStr.isEmpty()) {
  267. return;
  268. }
  269. for (String str : enchantListStr) {
  270. boolean foundMatch = false;
  271. for (Enchantment enchantment : Enchantment.values()) {
  272. if (enchantment.getKey().getKey().equalsIgnoreCase(str)) {
  273. permissiveList.add(enchantment);
  274. foundMatch = true;
  275. break;
  276. }
  277. }
  278. if (!foundMatch) {
  279. LogUtils.debug(mcMMO.p.getLogger(), "[Fishing Treasure Init] Could not find any enchantments which matched the user defined enchantment named: " + str);
  280. }
  281. }
  282. }
  283. private void loadEnchantments() {
  284. for (Rarity rarity : Rarity.values()) {
  285. if (!fishingEnchantments.containsKey(rarity)) {
  286. fishingEnchantments.put(rarity, (new ArrayList<>()));
  287. }
  288. ConfigurationSection enchantmentSection = config.getConfigurationSection("Enchantments_Rarity." + rarity.toString());
  289. if (enchantmentSection == null) {
  290. return;
  291. }
  292. for (String enchantmentName : enchantmentSection.getKeys(false)) {
  293. int level = config.getInt("Enchantments_Rarity." + rarity + "." + enchantmentName);
  294. Enchantment enchantment = EnchantmentUtils.getByName(enchantmentName);
  295. if (enchantment == null) {
  296. mcMMO.p.getLogger().info("Skipping invalid enchantment in '" + FILENAME + "', named:"
  297. + enchantmentName);
  298. continue;
  299. }
  300. fishingEnchantments.get(rarity).add(new EnchantmentTreasure(enchantment, level));
  301. }
  302. }
  303. }
  304. public boolean getInventoryStealEnabled() {
  305. return config.contains("Shake.PLAYER.INVENTORY");
  306. }
  307. public boolean getInventoryStealStacks() {
  308. return config.getBoolean("Shake.PLAYER.INVENTORY.Whole_Stacks");
  309. }
  310. public double getInventoryStealDropChance() {
  311. return config.getDouble("Shake.PLAYER.INVENTORY.Drop_Chance");
  312. }
  313. public int getInventoryStealDropLevel() {
  314. return config.getInt("Shake.PLAYER.INVENTORY.Drop_Level");
  315. }
  316. public double getItemDropRate(int tier, @NotNull Rarity rarity) {
  317. return config.getDouble("Item_Drop_Rates.Tier_" + tier + "." + rarity);
  318. }
  319. public double getEnchantmentDropRate(int tier, @NotNull Rarity rarity) {
  320. return config.getDouble("Enchantment_Drop_Rates.Tier_" + tier + "." + rarity);
  321. }
  322. }