|
@@ -1,325 +1,78 @@
|
|
|
package com.gmail.nossr50.util.random;
|
|
|
|
|
|
-import com.gmail.nossr50.config.AdvancedConfig;
|
|
|
-import com.gmail.nossr50.datatypes.skills.PrimarySkillType;
|
|
|
import com.gmail.nossr50.datatypes.skills.SubSkillType;
|
|
|
-import com.gmail.nossr50.events.skills.secondaryabilities.SubSkillEvent;
|
|
|
-import com.gmail.nossr50.events.skills.secondaryabilities.SubSkillRandomCheckEvent;
|
|
|
-import com.gmail.nossr50.util.EventUtils;
|
|
|
-import com.gmail.nossr50.util.Permissions;
|
|
|
-import com.gmail.nossr50.util.skills.SkillActivationType;
|
|
|
+import com.gmail.nossr50.util.skills.SkillUtils;
|
|
|
import org.bukkit.entity.Player;
|
|
|
import org.jetbrains.annotations.NotNull;
|
|
|
-import org.jetbrains.annotations.Nullable;
|
|
|
|
|
|
import java.text.DecimalFormat;
|
|
|
import java.util.concurrent.ThreadLocalRandom;
|
|
|
|
|
|
+//TODO: Normalize chance values
|
|
|
+//TODO: Test the 2 types of SkillProbabilityTypes
|
|
|
+//TODO: Update calls to this class and its members
|
|
|
public class RandomChanceUtil {
|
|
|
public static final @NotNull DecimalFormat percent = new DecimalFormat("##0.00%");
|
|
|
- //public static final DecimalFormat decimal = new DecimalFormat("##0.00");
|
|
|
- public static final double LINEAR_CURVE_VAR = 100.0D;
|
|
|
public static final double LUCKY_MODIFIER = 1.333D;
|
|
|
|
|
|
/**
|
|
|
- * This method is the final step in determining if a Sub-Skill / Secondary Skill in mcMMO successfully activates either from chance or otherwise
|
|
|
- * Random skills check for success based on numbers and then fire a cancellable event, if that event is not cancelled they succeed
|
|
|
- * non-RNG skills just fire the cancellable event and succeed if they go uncancelled
|
|
|
+ * Simulate an outcome on a probability and return true or false for the result of that outcome
|
|
|
*
|
|
|
- * @param skillActivationType this value represents what kind of activation procedures this sub-skill uses
|
|
|
- * @param subSkillType The identifier for this specific sub-skill
|
|
|
- * @param player The owner of this sub-skill
|
|
|
- * @return returns true if all conditions are met and the event is not cancelled
|
|
|
+ * @param probability target probability
|
|
|
+ * @return true if the probability succeeded, false if it failed
|
|
|
*/
|
|
|
- public static boolean isActivationSuccessful(@NotNull SkillActivationType skillActivationType, @NotNull SubSkillType subSkillType, @Nullable Player player) {
|
|
|
- switch (skillActivationType) {
|
|
|
- case RANDOM_LINEAR_100_SCALE_WITH_CAP:
|
|
|
- return checkRandomChanceExecutionSuccess(player, subSkillType, true);
|
|
|
- case RANDOM_STATIC_CHANCE:
|
|
|
- return checkRandomStaticChanceExecutionSuccess(player, subSkillType);
|
|
|
- case ALWAYS_FIRES:
|
|
|
- SubSkillEvent event = EventUtils.callSubSkillEvent(player, subSkillType);
|
|
|
- return !event.isCancelled();
|
|
|
- default:
|
|
|
- return false;
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
- public static double getActivationChance(@NotNull SkillActivationType skillActivationType, @NotNull SubSkillType subSkillType, @Nullable Player player, boolean luckyOverride) {
|
|
|
- switch (skillActivationType) {
|
|
|
- case RANDOM_LINEAR_100_SCALE_WITH_CAP:
|
|
|
- return getRandomChanceExecutionSuccess(player, subSkillType, true, luckyOverride);
|
|
|
- case RANDOM_STATIC_CHANCE:
|
|
|
- return getRandomStaticChanceExecutionSuccess(player, subSkillType, luckyOverride);
|
|
|
- default:
|
|
|
- return 0.1337;
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
- /**
|
|
|
- * Checks whether or not the random chance succeeds
|
|
|
- *
|
|
|
- * @return true if the random chance succeeds
|
|
|
- */
|
|
|
- public static boolean checkRandomChanceExecutionSuccess(@NotNull Player player, @NotNull PrimarySkillType primarySkillType, double chance) {
|
|
|
- //Check the odds
|
|
|
- chance *= 100;
|
|
|
-
|
|
|
- chance = addLuck(player, primarySkillType, chance);
|
|
|
-
|
|
|
- /*
|
|
|
- * Stuff like treasures can specify a drop chance from 0.05 to 100
|
|
|
- * Because of that we need to use a large int bound and multiply the chance by 100
|
|
|
- */
|
|
|
- return rollDice(chance, 10000);
|
|
|
- }
|
|
|
-
|
|
|
- public static boolean rollDice(double chanceOfSuccess, int bound) {
|
|
|
- return rollDice(chanceOfSuccess, bound, 1.0F);
|
|
|
- }
|
|
|
-
|
|
|
- public static boolean rollDice(double chanceOfSuccess, int bound, double resultModifier) {
|
|
|
- return chanceOfSuccess > (ThreadLocalRandom.current().nextInt(bound) * resultModifier);
|
|
|
+ public static boolean processProbability(@NotNull Probability probability) {
|
|
|
+ return isSuccessfulRoll(probability.getValue());
|
|
|
}
|
|
|
|
|
|
/**
|
|
|
- * Used for stuff like Excavation, Fishing, etc...
|
|
|
+ * Modify and then Simulate an outcome on a probability and return true or false for the result of that outcome
|
|
|
*
|
|
|
- * @param randomChance
|
|
|
- * @return
|
|
|
+ * @param probability target probability
|
|
|
+ * @param probabilityMultiplier probability will be multiplied by this before success is checked
|
|
|
+ * @return true if the probability succeeded, false if it failed
|
|
|
*/
|
|
|
- public static boolean checkRandomChanceExecutionSuccess(@NotNull RandomChanceSkillStatic randomChance, double resultModifier) {
|
|
|
- double chanceOfSuccess = calculateChanceOfSuccess(randomChance);
|
|
|
-
|
|
|
- //Check the odds
|
|
|
- return rollDice(chanceOfSuccess, 100, resultModifier);
|
|
|
+ public static boolean processProbability(@NotNull Probability probability, double probabilityMultiplier) {
|
|
|
+ double probabilityValue = probability.getValue() * probabilityMultiplier;
|
|
|
+ return isSuccessfulRoll(probabilityValue);
|
|
|
}
|
|
|
|
|
|
/**
|
|
|
- * Used for stuff like Excavation, Fishing, etc...
|
|
|
+ * Simulates a "roll of the dice"
|
|
|
+ * If the value passed is higher than the "random" value, than it is a successful roll
|
|
|
*
|
|
|
- * @param randomChance
|
|
|
- * @return
|
|
|
+ * @param probabilityValue probability value
|
|
|
+ * @return true for succeeding, false for failing
|
|
|
*/
|
|
|
- public static boolean checkRandomChanceExecutionSuccess(@NotNull RandomChanceSkillStatic randomChance) {
|
|
|
- return checkRandomChanceExecutionSuccess(randomChance, 1.0F);
|
|
|
- }
|
|
|
-
|
|
|
- public static boolean checkRandomChanceExecutionSuccess(@NotNull RandomChanceSkill randomChance) {
|
|
|
- double chanceOfSuccess = calculateChanceOfSuccess(randomChance);
|
|
|
-
|
|
|
- //Check the odds
|
|
|
- return rollDice(chanceOfSuccess, 100);
|
|
|
+ private static boolean isSuccessfulRoll(double probabilityValue) {
|
|
|
+ return probabilityValue >= ThreadLocalRandom.current().nextDouble(1.0D);
|
|
|
}
|
|
|
|
|
|
/**
|
|
|
- * Gets the Static Chance for something to activate
|
|
|
+ * Return a chance of success in "percentage" format, show to the player in UI elements
|
|
|
*
|
|
|
- * @param randomChance
|
|
|
- * @return
|
|
|
- */
|
|
|
- public static double getRandomChanceExecutionChance(@NotNull RandomChanceExecution randomChance) {
|
|
|
- return getChanceOfSuccess(randomChance.getXPos(), randomChance.getProbabilityCap(), LINEAR_CURVE_VAR);
|
|
|
- }
|
|
|
-
|
|
|
- public static double getRandomChanceExecutionChance(@NotNull RandomChanceExecution randomChance, boolean luckyOverride) {
|
|
|
- return getChanceOfSuccess(randomChance.getXPos(), randomChance.getProbabilityCap(), LINEAR_CURVE_VAR);
|
|
|
- }
|
|
|
-
|
|
|
- public static double getRandomChanceExecutionChance(@NotNull RandomChanceStatic randomChance) {
|
|
|
- double chanceOfSuccess = getChanceOfSuccess(randomChance.getXPos(), randomChance.getProbabilityCap(), LINEAR_CURVE_VAR);
|
|
|
-
|
|
|
- chanceOfSuccess = addLuck(randomChance.isLucky(), chanceOfSuccess);
|
|
|
-
|
|
|
- return chanceOfSuccess;
|
|
|
- }
|
|
|
-
|
|
|
- public static double calculateChanceOfSuccess(@NotNull RandomChanceSkill randomChance) {
|
|
|
- double skillLevel = randomChance.getSkillLevel();
|
|
|
- double maximumProbability = randomChance.getProbabilityCap();
|
|
|
- double maximumBonusLevel = randomChance.getMaximumBonusLevelCap();
|
|
|
-
|
|
|
- double chanceOfSuccess;
|
|
|
-
|
|
|
- if (skillLevel >= maximumBonusLevel) {
|
|
|
- //Chance of success is equal to the maximum probability if the maximum bonus level has been reached
|
|
|
- chanceOfSuccess = maximumProbability;
|
|
|
- } else {
|
|
|
- //Get chance of success
|
|
|
- chanceOfSuccess = getChanceOfSuccess(randomChance.getXPos(), maximumProbability, maximumBonusLevel);
|
|
|
- }
|
|
|
-
|
|
|
- //Add Luck
|
|
|
- chanceOfSuccess = addLuck(randomChance.isLucky(), chanceOfSuccess);
|
|
|
-
|
|
|
- return chanceOfSuccess;
|
|
|
- }
|
|
|
-
|
|
|
- public static double calculateChanceOfSuccess(@NotNull RandomChanceSkillStatic randomChance) {
|
|
|
- double chanceOfSuccess = getChanceOfSuccess(randomChance.getXPos(), 100, 100);
|
|
|
-
|
|
|
- //Add Luck
|
|
|
- chanceOfSuccess = addLuck(randomChance.isLucky(), chanceOfSuccess);
|
|
|
-
|
|
|
- return chanceOfSuccess;
|
|
|
- }
|
|
|
-
|
|
|
- /**
|
|
|
- * The formula for RNG success is determined like this
|
|
|
- * maximum probability * ( x / maxlevel )
|
|
|
+ * @param player target player
|
|
|
+ * @param subSkillType target subskill
|
|
|
+ * @param isLucky whether or not to apply luck modifiers
|
|
|
*
|
|
|
- * @return the chance of success from 0-100 (100 = guaranteed)
|
|
|
+ * @return "percentage" representation of success
|
|
|
*/
|
|
|
- private static int getChanceOfSuccess(double skillLevel, double maxProbability, double maxLevel) {
|
|
|
- //return (int) (x / (y / LINEAR_CURVE_VAR));
|
|
|
- return (int) (maxProbability * (skillLevel / maxLevel));
|
|
|
- // max probability * (weight/maxlevel) = chance of success
|
|
|
- }
|
|
|
-
|
|
|
- private static int getChanceOfSuccess(double x, double y) {
|
|
|
- return (int) (x / (y / LINEAR_CURVE_VAR));
|
|
|
- // max probability * (weight/maxlevel) = chance of success
|
|
|
- }
|
|
|
-
|
|
|
- public static double getRandomChanceExecutionSuccess(@Nullable Player player, @NotNull SubSkillType subSkillType, boolean hasCap) {
|
|
|
- RandomChanceSkill rcs = new RandomChanceSkill(player, subSkillType, hasCap);
|
|
|
- return calculateChanceOfSuccess(rcs);
|
|
|
- }
|
|
|
-
|
|
|
- public static double getRandomChanceExecutionSuccess(@Nullable Player player, @NotNull SubSkillType subSkillType, boolean hasCap, boolean luckyOverride) {
|
|
|
- RandomChanceSkill rcs = new RandomChanceSkill(player, subSkillType, hasCap, luckyOverride);
|
|
|
- return calculateChanceOfSuccess(rcs);
|
|
|
- }
|
|
|
-
|
|
|
- public static double getRandomStaticChanceExecutionSuccess(@Nullable Player player, @NotNull SubSkillType subSkillType, boolean luckyOverride) {
|
|
|
+ private static double chanceOfSuccessPercentage(@NotNull Player player, @NotNull SubSkillType subSkillType, boolean isLucky) {
|
|
|
try {
|
|
|
- return getRandomChanceExecutionChance(new RandomChanceSkillStatic(getStaticRandomChance(subSkillType), player, subSkillType, luckyOverride));
|
|
|
- } catch (InvalidStaticChance invalidStaticChance) {
|
|
|
- //Catch invalid static skills
|
|
|
- invalidStaticChance.printStackTrace();
|
|
|
- }
|
|
|
-
|
|
|
- return 0.1337; //Puts on shades
|
|
|
- }
|
|
|
-
|
|
|
- public static boolean checkRandomChanceExecutionSuccess(@Nullable Player player, @NotNull SubSkillType subSkillType, boolean hasCap) {
|
|
|
- return checkRandomChanceExecutionSuccess(new RandomChanceSkill(player, subSkillType, hasCap));
|
|
|
- }
|
|
|
-
|
|
|
- public static boolean checkRandomChanceExecutionSuccess(@Nullable Player player, @NotNull SubSkillType subSkillType) {
|
|
|
- return checkRandomChanceExecutionSuccess(new RandomChanceSkill(player, subSkillType));
|
|
|
- }
|
|
|
-
|
|
|
- public static boolean checkRandomChanceExecutionSuccess(@Nullable Player player, @NotNull SubSkillType subSkillType, boolean hasCap, double resultModifier) {
|
|
|
- return checkRandomChanceExecutionSuccess(new RandomChanceSkill(player, subSkillType, hasCap, resultModifier));
|
|
|
- }
|
|
|
-
|
|
|
- public static boolean checkRandomChanceExecutionSuccess(@Nullable Player player, @NotNull SubSkillType subSkillType, double resultModifier) {
|
|
|
- return checkRandomChanceExecutionSuccess(new RandomChanceSkill(player, subSkillType, resultModifier));
|
|
|
- }
|
|
|
+ Probability probability = SkillUtils.getSubSkillProbability(subSkillType, player);
|
|
|
+ //Probability values are on a 0-1 scale and need to be "transformed" into a 1-100 scale
|
|
|
+ double percentageValue = probability.getValue() * 100;
|
|
|
|
|
|
+ //Apply lucky modifier
|
|
|
+ if(isLucky) {
|
|
|
+ percentageValue *= LUCKY_MODIFIER;
|
|
|
+ }
|
|
|
|
|
|
- public static boolean checkRandomStaticChanceExecutionSuccess(@Nullable Player player, @NotNull SubSkillType subSkillType) {
|
|
|
- try {
|
|
|
- return checkRandomChanceExecutionSuccess(new RandomChanceSkillStatic(getStaticRandomChance(subSkillType), player, subSkillType));
|
|
|
+ return percentageValue;
|
|
|
} catch (InvalidStaticChance invalidStaticChance) {
|
|
|
- //Catch invalid static skills
|
|
|
invalidStaticChance.printStackTrace();
|
|
|
+ return 0;
|
|
|
}
|
|
|
-
|
|
|
- return false;
|
|
|
- }
|
|
|
-
|
|
|
- /**
|
|
|
- * Grabs static activation rolls for Secondary Abilities
|
|
|
- *
|
|
|
- * @param subSkillType The secondary ability to grab properties of
|
|
|
- * @return The static activation roll involved in the RNG calculation
|
|
|
- * @throws InvalidStaticChance if the skill has no defined static chance this exception will be thrown and you should know you're a naughty boy
|
|
|
- */
|
|
|
- public static double getStaticRandomChance(@NotNull SubSkillType subSkillType) throws InvalidStaticChance {
|
|
|
- switch (subSkillType) {
|
|
|
- case AXES_ARMOR_IMPACT:
|
|
|
- return AdvancedConfig.getInstance().getImpactChance();
|
|
|
- case AXES_GREATER_IMPACT:
|
|
|
- return AdvancedConfig.getInstance().getGreaterImpactChance();
|
|
|
- case TAMING_FAST_FOOD_SERVICE:
|
|
|
- return AdvancedConfig.getInstance().getFastFoodChance();
|
|
|
- default:
|
|
|
- throw new InvalidStaticChance();
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
- public static boolean sendSkillEvent(Player player, SubSkillType subSkillType, double activationChance) {
|
|
|
- SubSkillRandomCheckEvent event = new SubSkillRandomCheckEvent(player, subSkillType, activationChance);
|
|
|
- return !event.isCancelled();
|
|
|
- }
|
|
|
-
|
|
|
- public static String @NotNull [] calculateAbilityDisplayValues(@NotNull SkillActivationType skillActivationType, @NotNull Player player, @NotNull SubSkillType subSkillType) {
|
|
|
- double successChance = getActivationChance(skillActivationType, subSkillType, player, false);
|
|
|
- double successChanceLucky = getActivationChance(skillActivationType, subSkillType, player, true);
|
|
|
-
|
|
|
- String[] displayValues = new String[2];
|
|
|
-
|
|
|
- boolean isLucky = Permissions.lucky(player, subSkillType.getParentSkill());
|
|
|
-
|
|
|
- displayValues[0] = percent.format(Math.min(successChance, 100.0D) / 100.0D);
|
|
|
- displayValues[1] = isLucky ? percent.format(Math.min(successChanceLucky, 100.0D) / 100.0D) : null;
|
|
|
-
|
|
|
- return displayValues;
|
|
|
- }
|
|
|
-
|
|
|
- public static String @NotNull [] calculateAbilityDisplayValuesStatic(@NotNull Player player, @NotNull PrimarySkillType primarySkillType, double chance) {
|
|
|
- RandomChanceStatic rcs = new RandomChanceStatic(chance, false);
|
|
|
- double successChance = getRandomChanceExecutionChance(rcs);
|
|
|
-
|
|
|
- RandomChanceStatic rcs_lucky = new RandomChanceStatic(chance, true);
|
|
|
- double successChance_lucky = getRandomChanceExecutionChance(rcs_lucky);
|
|
|
-
|
|
|
- String[] displayValues = new String[2];
|
|
|
-
|
|
|
- boolean isLucky = Permissions.lucky(player, primarySkillType);
|
|
|
-
|
|
|
- displayValues[0] = percent.format(Math.min(successChance, 100.0D) / 100.0D);
|
|
|
- displayValues[1] = isLucky ? percent.format(Math.min(successChance_lucky, 100.0D) / 100.0D) : null;
|
|
|
-
|
|
|
- return displayValues;
|
|
|
}
|
|
|
|
|
|
- public static String @NotNull [] calculateAbilityDisplayValuesCustom(@NotNull SkillActivationType skillActivationType, @NotNull Player player, @NotNull SubSkillType subSkillType, double multiplier) {
|
|
|
- double successChance = getActivationChance(skillActivationType, subSkillType, player, false);
|
|
|
- double successChanceLucky = getActivationChance(skillActivationType, subSkillType, player, true);
|
|
|
- //TODO: Most likely incorrectly displays the value for graceful roll but gonna ignore for now...
|
|
|
- successChance *= multiplier; //Currently only used for graceful roll
|
|
|
- String[] displayValues = new String[2];
|
|
|
-
|
|
|
- boolean isLucky = Permissions.lucky(player, subSkillType.getParentSkill());
|
|
|
-
|
|
|
- displayValues[0] = percent.format(Math.min(successChance, 100.0D) / 100.0D);
|
|
|
- displayValues[1] = isLucky ? percent.format(Math.min(successChanceLucky, 100.0D) / 100.0D) : null;
|
|
|
-
|
|
|
- return displayValues;
|
|
|
- }
|
|
|
-
|
|
|
- public static double addLuck(@NotNull Player player, @NotNull PrimarySkillType primarySkillType, double chance) {
|
|
|
- if (Permissions.lucky(player, primarySkillType))
|
|
|
- return chance * LUCKY_MODIFIER;
|
|
|
- else
|
|
|
- return chance;
|
|
|
- }
|
|
|
-
|
|
|
- public static double addLuck(boolean isLucky, double chance) {
|
|
|
- if (isLucky)
|
|
|
- return chance * LUCKY_MODIFIER;
|
|
|
- else
|
|
|
- return chance;
|
|
|
- }
|
|
|
-
|
|
|
- public static double getMaximumProbability(@NotNull SubSkillType subSkillType) {
|
|
|
- return AdvancedConfig.getInstance().getMaximumProbability(subSkillType);
|
|
|
- }
|
|
|
-
|
|
|
- public static double getMaxBonusLevelCap(@NotNull SubSkillType subSkillType) {
|
|
|
- return AdvancedConfig.getInstance().getMaxBonusLevel(subSkillType);
|
|
|
- }
|
|
|
}
|