RandomChanceUtil.java 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318
  1. package com.gmail.nossr50.util.random;
  2. import com.gmail.nossr50.config.AdvancedConfig;
  3. import com.gmail.nossr50.datatypes.skills.PrimarySkillType;
  4. import com.gmail.nossr50.datatypes.skills.SubSkillType;
  5. import com.gmail.nossr50.datatypes.skills.subskills.AbstractSubSkill;
  6. import com.gmail.nossr50.events.skills.secondaryabilities.SubSkillEvent;
  7. import com.gmail.nossr50.events.skills.secondaryabilities.SubSkillRandomCheckEvent;
  8. import com.gmail.nossr50.util.EventUtils;
  9. import com.gmail.nossr50.util.Permissions;
  10. import com.gmail.nossr50.util.skills.SkillActivationType;
  11. import org.bukkit.entity.Player;
  12. import org.jetbrains.annotations.NotNull;
  13. import org.jetbrains.annotations.Nullable;
  14. import java.text.DecimalFormat;
  15. import java.util.concurrent.ThreadLocalRandom;
  16. public class RandomChanceUtil {
  17. public static final @NotNull DecimalFormat percent = new DecimalFormat("##0.00%");
  18. //public static final DecimalFormat decimal = new DecimalFormat("##0.00");
  19. public static final double LINEAR_CURVE_VAR = 100.0D;
  20. /**
  21. * This method is the final step in determining if a Sub-Skill / Secondary Skill in mcMMO successfully activates either from chance or otherwise
  22. * Random skills check for success based on numbers and then fire a cancellable event, if that event is not cancelled they succeed
  23. * non-RNG skills just fire the cancellable event and succeed if they go uncancelled
  24. *
  25. * @param skillActivationType this value represents what kind of activation procedures this sub-skill uses
  26. * @param subSkillType The identifier for this specific sub-skill
  27. * @param player The owner of this sub-skill
  28. * @return returns true if all conditions are met and the event is not cancelled
  29. */
  30. public static boolean isActivationSuccessful(@NotNull SkillActivationType skillActivationType, @NotNull SubSkillType subSkillType, @Nullable Player player) {
  31. switch (skillActivationType) {
  32. case RANDOM_LINEAR_100_SCALE_WITH_CAP:
  33. return checkRandomChanceExecutionSuccess(player, subSkillType, true);
  34. case RANDOM_STATIC_CHANCE:
  35. return checkRandomStaticChanceExecutionSuccess(player, subSkillType);
  36. case ALWAYS_FIRES:
  37. SubSkillEvent event = EventUtils.callSubSkillEvent(player, subSkillType);
  38. return !event.isCancelled();
  39. default:
  40. return false;
  41. }
  42. }
  43. public static double getActivationChance(@NotNull SkillActivationType skillActivationType, @NotNull SubSkillType subSkillType, @Nullable Player player) {
  44. switch (skillActivationType) {
  45. case RANDOM_LINEAR_100_SCALE_WITH_CAP:
  46. return getRandomChanceExecutionSuccess(player, subSkillType, true);
  47. case RANDOM_STATIC_CHANCE:
  48. return getRandomStaticChanceExecutionSuccess(player, subSkillType);
  49. default:
  50. return 0.1337;
  51. }
  52. }
  53. /**
  54. * Checks whether or not the random chance succeeds
  55. *
  56. * @return true if the random chance succeeds
  57. */
  58. public static boolean checkRandomChanceExecutionSuccess(@NotNull Player player, @NotNull PrimarySkillType primarySkillType, double chance) {
  59. //Check the odds
  60. chance *= 100;
  61. chance = addLuck(player, primarySkillType, chance);
  62. /*
  63. * Stuff like treasures can specify a drop chance from 0.05 to 100
  64. * Because of that we need to use a large int bound and multiply the chance by 100
  65. */
  66. return rollDice(chance, 10000);
  67. }
  68. public static boolean rollDice(double chanceOfSuccess, int bound) {
  69. return rollDice(chanceOfSuccess, bound, 1.0F);
  70. }
  71. public static boolean rollDice(double chanceOfSuccess, int bound, double resultModifier) {
  72. return chanceOfSuccess > (ThreadLocalRandom.current().nextInt(bound) * resultModifier);
  73. }
  74. /**
  75. * Used for stuff like Excavation, Fishing, etc...
  76. *
  77. * @param randomChance
  78. * @return
  79. */
  80. public static boolean checkRandomChanceExecutionSuccess(@NotNull RandomChanceSkillStatic randomChance, double resultModifier) {
  81. double chanceOfSuccess = calculateChanceOfSuccess(randomChance);
  82. //Check the odds
  83. return rollDice(chanceOfSuccess, 100, resultModifier);
  84. }
  85. /**
  86. * Used for stuff like Excavation, Fishing, etc...
  87. *
  88. * @param randomChance
  89. * @return
  90. */
  91. public static boolean checkRandomChanceExecutionSuccess(@NotNull RandomChanceSkillStatic randomChance) {
  92. return checkRandomChanceExecutionSuccess(randomChance, 1.0F);
  93. }
  94. public static boolean checkRandomChanceExecutionSuccess(@NotNull RandomChanceSkill randomChance) {
  95. double chanceOfSuccess = calculateChanceOfSuccess(randomChance);
  96. //Check the odds
  97. return rollDice(chanceOfSuccess, 100);
  98. }
  99. /*public static double getRandomChanceExecutionChance(RandomChanceSkill randomChance)
  100. {
  101. double chanceOfSuccess = calculateChanceOfSuccess(randomChance);
  102. return chanceOfSuccess;
  103. }*/
  104. /**
  105. * Gets the Static Chance for something to activate
  106. *
  107. * @param randomChance
  108. * @return
  109. */
  110. public static double getRandomChanceExecutionChance(@NotNull RandomChanceExecution randomChance) {
  111. return getChanceOfSuccess(randomChance.getXPos(), randomChance.getProbabilityCap(), LINEAR_CURVE_VAR);
  112. }
  113. public static double getRandomChanceExecutionChance(@NotNull RandomChanceStatic randomChance) {
  114. double chanceOfSuccess = getChanceOfSuccess(randomChance.getXPos(), randomChance.getProbabilityCap(), LINEAR_CURVE_VAR);
  115. chanceOfSuccess = addLuck(randomChance.isLucky(), chanceOfSuccess);
  116. return chanceOfSuccess;
  117. }
  118. /*private static double calculateChanceOfSuccess(RandomChanceStatic randomChance) {
  119. double chanceOfSuccess = getChanceOfSuccess(randomChance.getXPos(), randomChance.getProbabilityCap());
  120. return chanceOfSuccess;
  121. }*/
  122. private static double calculateChanceOfSuccess(@NotNull RandomChanceSkill randomChance) {
  123. double skillLevel = randomChance.getSkillLevel();
  124. double maximumProbability = randomChance.getProbabilityCap();
  125. double maximumBonusLevel = randomChance.getMaximumBonusLevelCap();
  126. double chanceOfSuccess;
  127. if (skillLevel >= maximumBonusLevel) {
  128. //Chance of success is equal to the maximum probability if the maximum bonus level has been reached
  129. chanceOfSuccess = maximumProbability;
  130. } else {
  131. //Get chance of success
  132. chanceOfSuccess = getChanceOfSuccess(randomChance.getXPos(), maximumProbability, maximumBonusLevel);
  133. }
  134. //Add Luck
  135. chanceOfSuccess = addLuck(randomChance.isLucky(), chanceOfSuccess);
  136. return chanceOfSuccess;
  137. }
  138. private static double calculateChanceOfSuccess(@NotNull RandomChanceSkillStatic randomChance) {
  139. double chanceOfSuccess = getChanceOfSuccess(randomChance.getXPos(), 100, 100);
  140. //Add Luck
  141. chanceOfSuccess = addLuck(randomChance.isLucky(), chanceOfSuccess);
  142. return chanceOfSuccess;
  143. }
  144. /**
  145. * The formula for RNG success is determined like this
  146. * maximum probability * ( x / maxlevel )
  147. *
  148. * @return the chance of success from 0-100 (100 = guaranteed)
  149. */
  150. private static int getChanceOfSuccess(double skillLevel, double maxProbability, double maxLevel) {
  151. //return (int) (x / (y / LINEAR_CURVE_VAR));
  152. return (int) (maxProbability * (skillLevel / maxLevel));
  153. // max probability * (weight/maxlevel) = chance of success
  154. }
  155. private static int getChanceOfSuccess(double x, double y) {
  156. return (int) (x / (y / LINEAR_CURVE_VAR));
  157. // max probability * (weight/maxlevel) = chance of success
  158. }
  159. public static double getRandomChanceExecutionSuccess(@Nullable Player player, @NotNull SubSkillType subSkillType, boolean hasCap) {
  160. RandomChanceSkill rcs = new RandomChanceSkill(player, subSkillType, hasCap);
  161. return calculateChanceOfSuccess(rcs);
  162. }
  163. public static double getRandomStaticChanceExecutionSuccess(@Nullable Player player, @NotNull SubSkillType subSkillType) {
  164. try {
  165. return getRandomChanceExecutionChance(new RandomChanceSkillStatic(getStaticRandomChance(subSkillType), player, subSkillType));
  166. } catch (InvalidStaticChance invalidStaticChance) {
  167. //Catch invalid static skills
  168. invalidStaticChance.printStackTrace();
  169. }
  170. return 0.1337; //Puts on shades
  171. }
  172. public static boolean checkRandomChanceExecutionSuccess(@Nullable Player player, @NotNull SubSkillType subSkillType, boolean hasCap) {
  173. return checkRandomChanceExecutionSuccess(new RandomChanceSkill(player, subSkillType, hasCap));
  174. }
  175. public static boolean checkRandomChanceExecutionSuccess(@Nullable Player player, @NotNull SubSkillType subSkillType) {
  176. return checkRandomChanceExecutionSuccess(new RandomChanceSkill(player, subSkillType));
  177. }
  178. public static boolean checkRandomChanceExecutionSuccess(@Nullable Player player, @NotNull SubSkillType subSkillType, boolean hasCap, double resultModifier) {
  179. return checkRandomChanceExecutionSuccess(new RandomChanceSkill(player, subSkillType, hasCap, resultModifier));
  180. }
  181. public static boolean checkRandomChanceExecutionSuccess(@Nullable Player player, @NotNull SubSkillType subSkillType, double resultModifier) {
  182. return checkRandomChanceExecutionSuccess(new RandomChanceSkill(player, subSkillType, resultModifier));
  183. }
  184. public static boolean checkRandomStaticChanceExecutionSuccess(@Nullable Player player, @NotNull SubSkillType subSkillType) {
  185. try {
  186. return checkRandomChanceExecutionSuccess(new RandomChanceSkillStatic(getStaticRandomChance(subSkillType), player, subSkillType));
  187. } catch (InvalidStaticChance invalidStaticChance) {
  188. //Catch invalid static skills
  189. invalidStaticChance.printStackTrace();
  190. }
  191. return false;
  192. }
  193. /**
  194. * Grabs static activation rolls for Secondary Abilities
  195. *
  196. * @param subSkillType The secondary ability to grab properties of
  197. * @return The static activation roll involved in the RNG calculation
  198. * @throws InvalidStaticChance if the skill has no defined static chance this exception will be thrown and you should know you're a naughty boy
  199. */
  200. public static double getStaticRandomChance(@NotNull SubSkillType subSkillType) throws InvalidStaticChance {
  201. switch (subSkillType) {
  202. case AXES_ARMOR_IMPACT:
  203. return AdvancedConfig.getInstance().getImpactChance();
  204. case AXES_GREATER_IMPACT:
  205. return AdvancedConfig.getInstance().getGreaterImpactChance();
  206. case TAMING_FAST_FOOD_SERVICE:
  207. return AdvancedConfig.getInstance().getFastFoodChance();
  208. default:
  209. throw new InvalidStaticChance();
  210. }
  211. }
  212. public static boolean sendSkillEvent(Player player, SubSkillType subSkillType, double activationChance) {
  213. SubSkillRandomCheckEvent event = new SubSkillRandomCheckEvent(player, subSkillType, activationChance);
  214. return !event.isCancelled();
  215. }
  216. public static String @NotNull [] calculateAbilityDisplayValues(@NotNull SkillActivationType skillActivationType, @NotNull Player player, @NotNull SubSkillType subSkillType) {
  217. double successChance = getActivationChance(skillActivationType, subSkillType, player);
  218. String[] displayValues = new String[2];
  219. boolean isLucky = Permissions.lucky(player, subSkillType.getParentSkill());
  220. displayValues[0] = percent.format(Math.min(successChance, 100.0D) / 100.0D);
  221. displayValues[1] = isLucky ? percent.format(Math.min(successChance * 1.3333D, 100.0D) / 100.0D) : null;
  222. return displayValues;
  223. }
  224. public static String @NotNull [] calculateAbilityDisplayValuesStatic(@NotNull Player player, @NotNull PrimarySkillType primarySkillType, double chance) {
  225. RandomChanceStatic rcs = new RandomChanceStatic(chance, false);
  226. double successChance = getRandomChanceExecutionChance(rcs);
  227. RandomChanceStatic rcs_lucky = new RandomChanceStatic(chance, true);
  228. double successChance_lucky = getRandomChanceExecutionChance(rcs_lucky);
  229. String[] displayValues = new String[2];
  230. boolean isLucky = Permissions.lucky(player, primarySkillType);
  231. displayValues[0] = percent.format(Math.min(successChance, 100.0D) / 100.0D);
  232. displayValues[1] = isLucky ? percent.format(Math.min(successChance_lucky, 100.0D) / 100.0D) : null;
  233. return displayValues;
  234. }
  235. public static String @NotNull [] calculateAbilityDisplayValuesCustom(@NotNull SkillActivationType skillActivationType, @NotNull Player player, @NotNull SubSkillType subSkillType, double multiplier) {
  236. double successChance = getActivationChance(skillActivationType, subSkillType, player);
  237. successChance *= multiplier; //Currently only used for graceful roll
  238. String[] displayValues = new String[2];
  239. //TODO: Account for lucky in this
  240. boolean isLucky = Permissions.lucky(player, subSkillType.getParentSkill());
  241. displayValues[0] = percent.format(Math.min(successChance, 100.0D) / 100.0D);
  242. displayValues[1] = isLucky ? percent.format(Math.min(successChance * 1.3333D, 100.0D) / 100.0D) : null;
  243. return displayValues;
  244. }
  245. public static double addLuck(@NotNull Player player, @NotNull PrimarySkillType primarySkillType, double chance) {
  246. if (Permissions.lucky(player, primarySkillType))
  247. return chance * 1.333D;
  248. else
  249. return chance;
  250. }
  251. public static double addLuck(boolean isLucky, double chance) {
  252. if (isLucky)
  253. return chance * 1.333D;
  254. else
  255. return chance;
  256. }
  257. }