SkillUtils.java 19 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489
  1. package com.gmail.nossr50.util.skills;
  2. import java.util.ArrayList;
  3. import java.util.List;
  4. import org.bukkit.Material;
  5. import org.bukkit.block.Block;
  6. import org.bukkit.enchantments.Enchantment;
  7. import org.bukkit.entity.Player;
  8. import org.bukkit.inventory.ItemStack;
  9. import org.bukkit.inventory.PlayerInventory;
  10. import org.bukkit.inventory.meta.ItemMeta;
  11. import org.bukkit.plugin.PluginManager;
  12. import org.bukkit.potion.PotionEffect;
  13. import org.bukkit.potion.PotionEffectType;
  14. import com.gmail.nossr50.mcMMO;
  15. import com.gmail.nossr50.config.AdvancedConfig;
  16. import com.gmail.nossr50.config.Config;
  17. import com.gmail.nossr50.config.HiddenConfig;
  18. import com.gmail.nossr50.datatypes.player.McMMOPlayer;
  19. import com.gmail.nossr50.datatypes.player.PlayerProfile;
  20. import com.gmail.nossr50.datatypes.skills.AbilityType;
  21. import com.gmail.nossr50.datatypes.skills.SkillType;
  22. import com.gmail.nossr50.datatypes.skills.ToolType;
  23. import com.gmail.nossr50.events.experience.McMMOPlayerLevelUpEvent;
  24. import com.gmail.nossr50.events.fake.FakeBlockBreakEvent;
  25. import com.gmail.nossr50.events.fake.FakeBlockDamageEvent;
  26. import com.gmail.nossr50.events.fake.FakePlayerAnimationEvent;
  27. import com.gmail.nossr50.events.skills.abilities.McMMOPlayerAbilityActivateEvent;
  28. import com.gmail.nossr50.locale.LocaleLoader;
  29. import com.gmail.nossr50.runnables.skills.AbilityDisableTask;
  30. import com.gmail.nossr50.runnables.skills.ToolLowerTask;
  31. import com.gmail.nossr50.util.ItemUtils;
  32. import com.gmail.nossr50.util.Misc;
  33. import com.gmail.nossr50.util.ModUtils;
  34. import com.gmail.nossr50.util.Permissions;
  35. import com.gmail.nossr50.util.StringUtils;
  36. import com.gmail.nossr50.util.player.UserManager;
  37. import com.gmail.nossr50.util.spout.SpoutUtils;
  38. public class SkillUtils {
  39. public static int handleFoodSkills(Player player, SkillType skill, int eventFoodLevel, int baseLevel, int maxLevel, int rankChange) {
  40. int skillLevel = UserManager.getPlayer(player).getProfile().getSkillLevel(skill);
  41. int currentFoodLevel = player.getFoodLevel();
  42. int foodChange = eventFoodLevel - currentFoodLevel;
  43. for (int i = baseLevel; i <= maxLevel; i += rankChange) {
  44. if (skillLevel >= i) {
  45. foodChange++;
  46. }
  47. }
  48. return currentFoodLevel + foodChange;
  49. }
  50. /**
  51. * Calculate the time remaining until the cooldown expires.
  52. *
  53. * @param deactivatedTimeStamp Time of deactivation
  54. * @param cooldown The length of the cooldown
  55. * @return the number of seconds remaining before the cooldown expires
  56. */
  57. public static int calculateTimeLeft(long deactivatedTimeStamp, int cooldown, Player player) {
  58. return (int) (((deactivatedTimeStamp + (PerksUtils.handleCooldownPerks(player, cooldown) * Misc.TIME_CONVERSION_FACTOR)) - System.currentTimeMillis()) / Misc.TIME_CONVERSION_FACTOR);
  59. }
  60. /**
  61. * Process activating abilities & readying the tool.
  62. *
  63. * @param player The player using the ability
  64. * @param skill The skill the ability is tied to
  65. */
  66. public static void activationCheck(Player player, SkillType skill) {
  67. if (Config.getInstance().getAbilitiesOnlyActivateWhenSneaking() && !player.isSneaking()) {
  68. return;
  69. }
  70. ItemStack inHand = player.getItemInHand();
  71. if (ModUtils.isCustomTool(inHand) && !ModUtils.getToolFromItemStack(inHand).isAbilityEnabled()) {
  72. return;
  73. }
  74. McMMOPlayer mcMMOPlayer = UserManager.getPlayer(player);
  75. if (!mcMMOPlayer.getAbilityUse()) {
  76. return;
  77. }
  78. for (AbilityType abilityType : AbilityType.values()) {
  79. if (mcMMOPlayer.getAbilityMode(abilityType)) {
  80. return;
  81. }
  82. }
  83. PlayerProfile playerProfile = mcMMOPlayer.getProfile();
  84. AbilityType ability = skill.getAbility();
  85. ToolType tool = skill.getTool();
  86. /*
  87. * Woodcutting & Axes need to be treated differently.
  88. * Basically the tool always needs to ready and we check to see if the cooldown is over when the user takes action
  89. */
  90. if (ability.getPermissions(player) && tool.inHand(inHand) && !mcMMOPlayer.getToolPreparationMode(tool)) {
  91. if (skill != SkillType.WOODCUTTING && skill != SkillType.AXES) {
  92. int timeRemaining = calculateTimeLeft(playerProfile.getSkillDATS(ability) * Misc.TIME_CONVERSION_FACTOR, ability.getCooldown(), player);
  93. if (!mcMMOPlayer.getAbilityMode(ability) && timeRemaining > 0) {
  94. player.sendMessage(LocaleLoader.getString("Skills.TooTired", timeRemaining));
  95. return;
  96. }
  97. }
  98. if (Config.getInstance().getAbilityMessagesEnabled()) {
  99. player.sendMessage(tool.getRaiseTool());
  100. }
  101. mcMMOPlayer.setToolPreparationATS(tool, System.currentTimeMillis());
  102. mcMMOPlayer.setToolPreparationMode(tool, true);
  103. new ToolLowerTask(mcMMOPlayer, tool).runTaskLaterAsynchronously(mcMMO.p, 4 * Misc.TICK_CONVERSION_FACTOR);
  104. }
  105. }
  106. /**
  107. * Check the XP of a skill.
  108. *
  109. * @param skillType The skill to check
  110. * @param player The player whose skill to check
  111. * @param profile The profile of the player whose skill to check
  112. */
  113. public static void xpCheckSkill(SkillType skillType, Player player, PlayerProfile profile) {
  114. int levelsGained = 0;
  115. float xpRemoved = 0;
  116. if (profile.getSkillXpLevelRaw(skillType) >= profile.getXpToLevel(skillType)) {
  117. McMMOPlayer mcMMOPlayer = UserManager.getPlayer(player);
  118. while (profile.getSkillXpLevelRaw(skillType) >= profile.getXpToLevel(skillType)) {
  119. if ((skillType.getMaxLevel() >= profile.getSkillLevel(skillType) + 1) && (Config.getInstance().getPowerLevelCap() >= mcMMOPlayer.getPowerLevel() + 1)) {
  120. int xp = profile.getXpToLevel(skillType);
  121. xpRemoved += xp;
  122. profile.removeXp(skillType, xp);
  123. levelsGained++;
  124. profile.skillUp(skillType, 1);
  125. }
  126. else {
  127. profile.addLevels(skillType, 0);
  128. }
  129. }
  130. McMMOPlayerLevelUpEvent eventToFire = new McMMOPlayerLevelUpEvent(player, skillType, levelsGained);
  131. mcMMO.p.getServer().getPluginManager().callEvent(eventToFire);
  132. if (eventToFire.isCancelled()) {
  133. profile.modifySkill(skillType, profile.getSkillLevel(skillType) - levelsGained);
  134. profile.setSkillXpLevel(skillType, profile.getSkillXpLevelRaw(skillType) + xpRemoved);
  135. return;
  136. }
  137. String capitalized = StringUtils.getCapitalized(skillType.toString());
  138. if (mcMMO.isSpoutEnabled()) {
  139. SpoutUtils.processLevelup(mcMMOPlayer, skillType, levelsGained);
  140. }
  141. else {
  142. player.sendMessage(LocaleLoader.getString(capitalized + ".Skillup", levelsGained, profile.getSkillLevel(skillType)));
  143. }
  144. }
  145. if (mcMMO.isSpoutEnabled()) {
  146. SpoutUtils.processXpGain(player, profile);
  147. }
  148. }
  149. /**
  150. * Checks if the given string represents a valid skill
  151. *
  152. * @param skillName The name of the skill to check
  153. * @return true if this is a valid skill, false otherwise
  154. */
  155. public static boolean isSkill(String skillName) {
  156. if (!Config.getInstance().getLocale().equalsIgnoreCase("en_US")) {
  157. return isLocalizedSkill(skillName);
  158. }
  159. return SkillType.getSkill(skillName) != null;
  160. }
  161. private static boolean isLocalizedSkill(String skillName) {
  162. for (SkillType skill : SkillType.values()) {
  163. if (skillName.equalsIgnoreCase(LocaleLoader.getString(StringUtils.getCapitalized(skill.toString()) + ".SkillName"))) {
  164. return true;
  165. }
  166. }
  167. return false;
  168. }
  169. public static String getSkillName(SkillType skill) {
  170. if (!Config.getInstance().getLocale().equalsIgnoreCase("en_US")) {
  171. return StringUtils.getCapitalized(LocaleLoader.getString(StringUtils.getCapitalized(skill.toString()) + ".SkillName"));
  172. }
  173. return StringUtils.getCapitalized(skill.toString());
  174. }
  175. /**
  176. * Check if the player has any combat skill permissions.
  177. *
  178. * @param player The player to check permissions for
  179. * @return true if the player has combat skills, false otherwise
  180. */
  181. public static boolean hasCombatSkills(Player player) {
  182. return Permissions.skillEnabled(player, SkillType.AXES)
  183. || Permissions.skillEnabled(player, SkillType.ARCHERY)
  184. || Permissions.skillEnabled(player, SkillType.SWORDS)
  185. || Permissions.skillEnabled(player, SkillType.TAMING)
  186. || Permissions.skillEnabled(player, SkillType.UNARMED);
  187. }
  188. /**
  189. * Check if the player has any gathering skill permissions.
  190. *
  191. * @param player The player to check permissions for
  192. * @return true if the player has gathering skills, false otherwise
  193. */
  194. public static boolean hasGatheringSkills(Player player) {
  195. return Permissions.skillEnabled(player, SkillType.EXCAVATION)
  196. || Permissions.skillEnabled(player, SkillType.FISHING)
  197. || Permissions.skillEnabled(player, SkillType.HERBALISM)
  198. || Permissions.skillEnabled(player, SkillType.MINING)
  199. || Permissions.skillEnabled(player, SkillType.WOODCUTTING);
  200. }
  201. /**
  202. * Check if the player has any misc skill permissions.
  203. *
  204. * @param player The player to check permissions for
  205. * @return true if the player has misc skills, false otherwise
  206. */
  207. public static boolean hasMiscSkills(Player player) {
  208. return Permissions.skillEnabled(player, SkillType.ACROBATICS)
  209. || Permissions.skillEnabled(player, SkillType.SMELTING)
  210. || Permissions.skillEnabled(player, SkillType.RANCHING)
  211. || Permissions.skillEnabled(player, SkillType.REPAIR);
  212. }
  213. /**
  214. * Check to see if an ability can be activated.
  215. *
  216. * @param mcMMOPlayer The player activating the ability
  217. * @param type The skill the ability is based on
  218. */
  219. public static void abilityCheck(McMMOPlayer mcMMOPlayer, SkillType type) {
  220. ToolType tool = type.getTool();
  221. AbilityType ability = type.getAbility();
  222. mcMMOPlayer.setToolPreparationMode(tool, false);
  223. Player player = mcMMOPlayer.getPlayer();
  224. PlayerProfile playerProfile = mcMMOPlayer.getProfile();
  225. int timeRemaining = calculateTimeLeft(playerProfile.getSkillDATS(ability) * Misc.TIME_CONVERSION_FACTOR, ability.getCooldown(), player);
  226. /*
  227. * Axes and Woodcutting are odd because they share the same tool.
  228. * We show them the too tired message when they take action.
  229. */
  230. if (type == SkillType.WOODCUTTING || type == SkillType.AXES) {
  231. if (!mcMMOPlayer.getAbilityMode(ability) && timeRemaining > 0) {
  232. player.sendMessage(LocaleLoader.getString("Skills.TooTired", timeRemaining));
  233. return;
  234. }
  235. }
  236. if (!mcMMOPlayer.getAbilityMode(ability) && timeRemaining <= 0) {
  237. McMMOPlayerAbilityActivateEvent event = new McMMOPlayerAbilityActivateEvent(player, type);
  238. mcMMO.p.getServer().getPluginManager().callEvent(event);
  239. if (event.isCancelled()) {
  240. return;
  241. }
  242. int ticks = PerksUtils.handleActivationPerks(player, 2 + (playerProfile.getSkillLevel(type) / AdvancedConfig.getInstance().getAbilityLength()), ability.getMaxTicks());
  243. ParticleEffectUtils.playAbilityEnabledEffect(player);
  244. if (mcMMOPlayer.useChatNotifications()) {
  245. player.sendMessage(ability.getAbilityOn());
  246. }
  247. SkillUtils.sendSkillMessage(player, ability.getAbilityPlayer(player));
  248. playerProfile.setSkillDATS(ability, System.currentTimeMillis() + (ticks * Misc.TIME_CONVERSION_FACTOR));
  249. mcMMOPlayer.setAbilityMode(ability, true);
  250. if (ability == AbilityType.SUPER_BREAKER || ability == AbilityType.GIGA_DRILL_BREAKER) {
  251. handleAbilitySpeedIncrease(player);
  252. }
  253. new AbilityDisableTask(mcMMOPlayer, ability).runTaskLater(mcMMO.p, ticks * Misc.TICK_CONVERSION_FACTOR);
  254. }
  255. }
  256. /**
  257. * Check to see if ability should be triggered.
  258. *
  259. * @param player The player using the ability
  260. * @param block The block modified by the ability
  261. * @param ability The ability to check
  262. * @return true if the ability should activate, false otherwise
  263. */
  264. public static boolean triggerCheck(Player player, Block block, AbilityType ability) {
  265. boolean activate = true;
  266. switch (ability) {
  267. case BERSERK:
  268. case BLOCK_CRACKER:
  269. case LEAF_BLOWER:
  270. if (!ability.blockCheck(block.getState())) {
  271. activate = false;
  272. break;
  273. }
  274. if (!blockBreakSimulate(block, player, true)) {
  275. activate = false;
  276. break;
  277. }
  278. break;
  279. case GIGA_DRILL_BREAKER:
  280. case SUPER_BREAKER:
  281. case GREEN_TERRA:
  282. if (!ability.blockCheck(block.getState())) {
  283. activate = false;
  284. break;
  285. }
  286. break;
  287. default:
  288. activate = false;
  289. break;
  290. }
  291. return activate;
  292. }
  293. public static void sendSkillMessage(Player player, String message) {
  294. for (Player otherPlayer : player.getWorld().getPlayers()) {
  295. if (otherPlayer != player && Misc.isNear(player.getLocation(), otherPlayer.getLocation(), Misc.SKILL_MESSAGE_MAX_SENDING_DISTANCE)) {
  296. otherPlayer.sendMessage(message);
  297. }
  298. }
  299. }
  300. public static void handleAbilitySpeedIncrease(Player player) {
  301. if (HiddenConfig.getInstance().useEnchantmentBuffs()) {
  302. ItemStack heldItem = player.getItemInHand();
  303. if (heldItem == null || heldItem.getType() == Material.AIR) {
  304. return;
  305. }
  306. int efficiencyLevel = heldItem.getEnchantmentLevel(Enchantment.DIG_SPEED);
  307. ItemMeta itemMeta = heldItem.getItemMeta();
  308. List<String> itemLore = new ArrayList<String>();
  309. if (itemMeta.hasLore()) {
  310. itemLore = itemMeta.getLore();
  311. }
  312. itemLore.add("mcMMO Ability Tool");
  313. itemMeta.addEnchant(Enchantment.DIG_SPEED, efficiencyLevel + AdvancedConfig.getInstance().getEnchantBuff(), true);
  314. itemMeta.setLore(itemLore);
  315. heldItem.setItemMeta(itemMeta);
  316. }
  317. else {
  318. int duration = 0;
  319. int amplifier = 0;
  320. if (player.hasPotionEffect(PotionEffectType.FAST_DIGGING)) {
  321. for (PotionEffect effect : player.getActivePotionEffects()) {
  322. if (effect.getType() == PotionEffectType.FAST_DIGGING) {
  323. duration = effect.getDuration();
  324. amplifier = effect.getAmplifier();
  325. break;
  326. }
  327. }
  328. }
  329. McMMOPlayer mcMMOPlayer = UserManager.getPlayer(player);
  330. SkillType skill = mcMMOPlayer.getAbilityMode(AbilityType.SUPER_BREAKER) ? SkillType.MINING : SkillType.EXCAVATION;
  331. int ticks = PerksUtils.handleActivationPerks(player, 2 + (mcMMOPlayer.getProfile().getSkillLevel(skill) / AdvancedConfig.getInstance().getAbilityLength()), skill.getAbility().getMaxTicks()) * Misc.TICK_CONVERSION_FACTOR;
  332. PotionEffect abilityBuff = new PotionEffect(PotionEffectType.FAST_DIGGING, duration + ticks, amplifier + 10);
  333. player.addPotionEffect(abilityBuff, true);
  334. }
  335. }
  336. public static void handleAbilitySpeedDecrease(Player player) {
  337. if (HiddenConfig.getInstance().useEnchantmentBuffs()) {
  338. PlayerInventory playerInventory = player.getInventory();
  339. for (int i = 0; i < playerInventory.getContents().length; i++) {
  340. ItemStack item = playerInventory.getItem(i);
  341. playerInventory.setItem(i, removeAbilityBuff(item));
  342. }
  343. }
  344. }
  345. public static ItemStack removeAbilityBuff(ItemStack item) {
  346. if (item == null || item.getType() == Material.AIR) {
  347. return item;
  348. }
  349. if (!ItemUtils.isPickaxe(item) && !ItemUtils.isShovel(item)) {
  350. return item;
  351. }
  352. if (item.containsEnchantment(Enchantment.DIG_SPEED)) {
  353. ItemMeta itemMeta = item.getItemMeta();
  354. if (itemMeta.hasLore()) {
  355. List<String> itemLore = itemMeta.getLore();
  356. if (itemLore.remove("mcMMO Ability Tool")) {
  357. int efficiencyLevel = item.getEnchantmentLevel(Enchantment.DIG_SPEED);
  358. if (efficiencyLevel <= AdvancedConfig.getInstance().getEnchantBuff()) {
  359. itemMeta.removeEnchant(Enchantment.DIG_SPEED);
  360. }
  361. else {
  362. itemMeta.addEnchant(Enchantment.DIG_SPEED, efficiencyLevel - AdvancedConfig.getInstance().getEnchantBuff(), true);
  363. }
  364. itemMeta.setLore(itemLore);
  365. item.setItemMeta(itemMeta);
  366. }
  367. }
  368. }
  369. return item;
  370. }
  371. /**
  372. * Simulate a block break event.
  373. *
  374. * @param block The block to break
  375. * @param player The player breaking the block
  376. * @param shouldArmSwing true if an armswing event should be fired, false otherwise
  377. * @return true if the event wasn't cancelled, false otherwise
  378. */
  379. public static boolean blockBreakSimulate(Block block, Player player, boolean shouldArmSwing) {
  380. PluginManager pluginManger = mcMMO.p.getServer().getPluginManager();
  381. // Support for NoCheat
  382. if (shouldArmSwing) {
  383. pluginManger.callEvent(new FakePlayerAnimationEvent(player));
  384. }
  385. FakeBlockDamageEvent damageEvent = new FakeBlockDamageEvent(player, block, player.getItemInHand(), true);
  386. pluginManger.callEvent(damageEvent);
  387. FakeBlockBreakEvent breakEvent = new FakeBlockBreakEvent(block, player);
  388. pluginManger.callEvent(breakEvent);
  389. return !damageEvent.isCancelled() && !breakEvent.isCancelled();
  390. }
  391. public static boolean activationSuccessful(Player player, SkillType skill, double maxChance, int maxLevel) {
  392. int skillLevel = UserManager.getPlayer(player).getProfile().getSkillLevel(skill);
  393. int activationChance = PerksUtils.handleLuckyPerks(player, skill);
  394. double chance = (maxChance / maxLevel) * Math.min(skillLevel, maxLevel);
  395. return chance > Misc.getRandom().nextInt(activationChance);
  396. }
  397. public static boolean activationSuccessful(int skillLevel, int activationChance, double maxChance, int maxLevel) {
  398. return ((maxChance / maxLevel) * Math.min(skillLevel, maxLevel)) > Misc.getRandom().nextInt(activationChance);
  399. }
  400. public static boolean treasureDropSuccessful(double dropChance, int activationChance) {
  401. return dropChance > Misc.getRandom().nextDouble() * activationChance;
  402. }
  403. }