Browse Source

RandomChanceUtil refactor part 1

nossr50 4 years ago
parent
commit
f7640938df
43 changed files with 475 additions and 723 deletions
  1. 1 0
      Changelog.txt
  2. 9 0
      src/main/java/com/gmail/nossr50/api/exceptions/ValueOutOfBoundsException.java
  3. 5 7
      src/main/java/com/gmail/nossr50/commands/skills/AcrobaticsCommand.java
  4. 2 3
      src/main/java/com/gmail/nossr50/commands/skills/ArcheryCommand.java
  5. 1 2
      src/main/java/com/gmail/nossr50/commands/skills/AxesCommand.java
  6. 4 5
      src/main/java/com/gmail/nossr50/commands/skills/HerbalismCommand.java
  7. 6 8
      src/main/java/com/gmail/nossr50/commands/skills/MiningCommand.java
  8. 1 2
      src/main/java/com/gmail/nossr50/commands/skills/RepairCommand.java
  9. 1 1
      src/main/java/com/gmail/nossr50/commands/skills/SkillCommand.java
  10. 2 3
      src/main/java/com/gmail/nossr50/commands/skills/SmeltingCommand.java
  11. 2 3
      src/main/java/com/gmail/nossr50/commands/skills/SwordsCommand.java
  12. 1 2
      src/main/java/com/gmail/nossr50/commands/skills/TamingCommand.java
  13. 3 4
      src/main/java/com/gmail/nossr50/commands/skills/UnarmedCommand.java
  14. 2 3
      src/main/java/com/gmail/nossr50/commands/skills/WoodcuttingCommand.java
  15. 16 17
      src/main/java/com/gmail/nossr50/datatypes/skills/subskills/acrobatics/Roll.java
  16. 40 4
      src/main/java/com/gmail/nossr50/datatypes/treasure/Treasure.java
  17. 2 3
      src/main/java/com/gmail/nossr50/listeners/EntityListener.java
  18. 1 3
      src/main/java/com/gmail/nossr50/skills/acrobatics/AcrobaticsManager.java
  19. 3 3
      src/main/java/com/gmail/nossr50/skills/archery/ArcheryManager.java
  20. 5 5
      src/main/java/com/gmail/nossr50/skills/axes/AxesManager.java
  21. 1 1
      src/main/java/com/gmail/nossr50/skills/excavation/ExcavationManager.java
  22. 7 6
      src/main/java/com/gmail/nossr50/skills/fishing/FishingManager.java
  23. 4 6
      src/main/java/com/gmail/nossr50/skills/herbalism/HerbalismManager.java
  24. 3 5
      src/main/java/com/gmail/nossr50/skills/repair/RepairManager.java
  25. 3 4
      src/main/java/com/gmail/nossr50/skills/salvage/SalvageManager.java
  26. 2 3
      src/main/java/com/gmail/nossr50/skills/smelting/SmeltingManager.java
  27. 3 4
      src/main/java/com/gmail/nossr50/skills/swords/SwordsManager.java
  28. 4 5
      src/main/java/com/gmail/nossr50/skills/taming/TamingManager.java
  29. 6 6
      src/main/java/com/gmail/nossr50/skills/unarmed/UnarmedManager.java
  30. 3 4
      src/main/java/com/gmail/nossr50/skills/woodcutting/WoodcuttingManager.java
  31. 1 2
      src/main/java/com/gmail/nossr50/util/BlockUtils.java
  32. 22 0
      src/main/java/com/gmail/nossr50/util/Misc.java
  33. 14 0
      src/main/java/com/gmail/nossr50/util/random/Probability.java
  34. 77 0
      src/main/java/com/gmail/nossr50/util/random/ProbabilityFactory.java
  35. 63 0
      src/main/java/com/gmail/nossr50/util/random/ProbabilityImpl.java
  36. 0 18
      src/main/java/com/gmail/nossr50/util/random/RandomChanceExecution.java
  37. 0 176
      src/main/java/com/gmail/nossr50/util/random/RandomChanceSkill.java
  38. 0 61
      src/main/java/com/gmail/nossr50/util/random/RandomChanceSkillStatic.java
  39. 0 38
      src/main/java/com/gmail/nossr50/util/random/RandomChanceStatic.java
  40. 37 284
      src/main/java/com/gmail/nossr50/util/random/RandomChanceUtil.java
  41. 6 0
      src/main/java/com/gmail/nossr50/util/random/SkillProbabilityType.java
  42. 90 0
      src/main/java/com/gmail/nossr50/util/skills/SkillUtils.java
  43. 22 22
      src/test/java/com/gmail/nossr50/util/random/RandomChanceTest.java

+ 1 - 0
Changelog.txt

@@ -27,6 +27,7 @@ Version 2.1.175
     Added 'Woodcutting.SubSkill.CleanCuts.Name' to locale
     Added 'Woodcutting.SubSkill.CleanCuts.Stat' to locale
     Added 'Woodcutting.SubSkill.CleanCuts.Description' to locale
+    (Codebase) Major refactoring for how random chance was handled in the code
 
     Added 'General.PowerLevel.Skill_Mastery.Enabled' to config.yml which is used to enable or disable the mastery skills (will also disable the new power level command)
 

+ 9 - 0
src/main/java/com/gmail/nossr50/api/exceptions/ValueOutOfBoundsException.java

@@ -0,0 +1,9 @@
+package com.gmail.nossr50.api.exceptions;
+
+import org.jetbrains.annotations.NotNull;
+
+public class ValueOutOfBoundsException extends RuntimeException {
+    public ValueOutOfBoundsException(@NotNull String message) {
+        super(message);
+    }
+}

+ 5 - 7
src/main/java/com/gmail/nossr50/commands/skills/AcrobaticsCommand.java

@@ -6,9 +6,7 @@ import com.gmail.nossr50.datatypes.skills.subskills.AbstractSubSkill;
 import com.gmail.nossr50.listeners.InteractionManager;
 import com.gmail.nossr50.locale.LocaleLoader;
 import com.gmail.nossr50.util.Permissions;
-import com.gmail.nossr50.util.random.RandomChanceSkill;
 import com.gmail.nossr50.util.random.RandomChanceUtil;
-import com.gmail.nossr50.util.skills.SkillActivationType;
 import com.gmail.nossr50.util.text.TextComponentFactory;
 import net.kyori.adventure.text.Component;
 import org.bukkit.entity.Player;
@@ -31,7 +29,7 @@ public class AcrobaticsCommand extends SkillCommand {
     protected void dataCalculations(Player player, float skillValue) {
         // ACROBATICS_DODGE
         if (canDodge) {
-            String[] dodgeStrings = getAbilityDisplayValues(SkillActivationType.RANDOM_LINEAR_100_SCALE_WITH_CAP, player, SubSkillType.ACROBATICS_DODGE);
+            String[] dodgeStrings = getAbilityDisplayValues(SkillProbabilityType.DYNAMIC_CONFIGURABLE, player, SubSkillType.ACROBATICS_DODGE);
             dodgeChance = dodgeStrings[0];
             dodgeChanceLucky = dodgeStrings[1];
         }
@@ -61,18 +59,18 @@ public class AcrobaticsCommand extends SkillCommand {
                 double rollChance, graceChance;
 
                 //Chance to roll at half
-                RandomChanceSkill roll_rcs  = new RandomChanceSkill(player, SubSkillType.ACROBATICS_ROLL);
+                SkillProbabilityWrapper roll_rcs  = new SkillProbabilityWrapper(player, SubSkillType.ACROBATICS_ROLL);
 
                 //Chance to graceful roll
-                RandomChanceSkill grace_rcs = new RandomChanceSkill(player, SubSkillType.ACROBATICS_ROLL);
-                grace_rcs.setSkillLevel(grace_rcs.getSkillLevel() * 2); //Double Odds
+                SkillProbabilityWrapper grace_rcs = new SkillProbabilityWrapper(player, SubSkillType.ACROBATICS_ROLL);
+                grace_rcs.setxPos(grace_rcs.getxPos() * 2); //Double Odds
 
                 //Chance Stat Calculations
                 rollChance       = RandomChanceUtil.getRandomChanceExecutionChance(roll_rcs);
                 graceChance      = RandomChanceUtil.getRandomChanceExecutionChance(grace_rcs);
                 //damageThreshold  = AdvancedConfig.getInstance().getRollDamageThreshold();
 
-                String[] rollStrings = getAbilityDisplayValues(SkillActivationType.RANDOM_LINEAR_100_SCALE_WITH_CAP, player, SubSkillType.ACROBATICS_ROLL);
+                String[] rollStrings = getAbilityDisplayValues(SkillProbabilityType.DYNAMIC_CONFIGURABLE, player, SubSkillType.ACROBATICS_ROLL);
 
                 //Format
                 double rollChanceLucky  = rollChance * 1.333D;

+ 2 - 3
src/main/java/com/gmail/nossr50/commands/skills/ArcheryCommand.java

@@ -6,7 +6,6 @@ import com.gmail.nossr50.locale.LocaleLoader;
 import com.gmail.nossr50.skills.archery.Archery;
 import com.gmail.nossr50.util.Permissions;
 import com.gmail.nossr50.util.skills.CombatUtils;
-import com.gmail.nossr50.util.skills.SkillActivationType;
 import com.gmail.nossr50.util.text.TextComponentFactory;
 import net.kyori.adventure.text.Component;
 import org.bukkit.entity.Player;
@@ -33,14 +32,14 @@ public class ArcheryCommand extends SkillCommand {
     protected void dataCalculations(Player player, float skillValue) {
         // ARCHERY_ARROW_RETRIEVAL
         if (canRetrieve) {
-            String[] retrieveStrings = getAbilityDisplayValues(SkillActivationType.RANDOM_LINEAR_100_SCALE_WITH_CAP, player, SubSkillType.ARCHERY_ARROW_RETRIEVAL);
+            String[] retrieveStrings = getAbilityDisplayValues(SkillProbabilityType.DYNAMIC_CONFIGURABLE, player, SubSkillType.ARCHERY_ARROW_RETRIEVAL);
             retrieveChance = retrieveStrings[0];
             retrieveChanceLucky = retrieveStrings[1];
         }
         
         // ARCHERY_DAZE
         if (canDaze) {
-            String[] dazeStrings = getAbilityDisplayValues(SkillActivationType.RANDOM_LINEAR_100_SCALE_WITH_CAP, player, SubSkillType.ARCHERY_DAZE);
+            String[] dazeStrings = getAbilityDisplayValues(SkillProbabilityType.DYNAMIC_CONFIGURABLE, player, SubSkillType.ARCHERY_DAZE);
             dazeChance = dazeStrings[0];
             dazeChanceLucky = dazeStrings[1];
         }

+ 1 - 2
src/main/java/com/gmail/nossr50/commands/skills/AxesCommand.java

@@ -8,7 +8,6 @@ import com.gmail.nossr50.util.Permissions;
 import com.gmail.nossr50.util.player.UserManager;
 import com.gmail.nossr50.util.skills.CombatUtils;
 import com.gmail.nossr50.util.skills.RankUtils;
-import com.gmail.nossr50.util.skills.SkillActivationType;
 import com.gmail.nossr50.util.text.TextComponentFactory;
 import net.kyori.adventure.text.Component;
 import org.bukkit.entity.Player;
@@ -48,7 +47,7 @@ public class AxesCommand extends SkillCommand {
         
         // CRITICAL HIT
         if (canCritical) {
-            String[] criticalHitStrings = getAbilityDisplayValues(SkillActivationType.RANDOM_LINEAR_100_SCALE_WITH_CAP, player, SubSkillType.AXES_CRITICAL_STRIKES);
+            String[] criticalHitStrings = getAbilityDisplayValues(SkillProbabilityType.DYNAMIC_CONFIGURABLE, player, SubSkillType.AXES_CRITICAL_STRIKES);
             critChance = criticalHitStrings[0];
             critChanceLucky = criticalHitStrings[1];
         }

+ 4 - 5
src/main/java/com/gmail/nossr50/commands/skills/HerbalismCommand.java

@@ -5,7 +5,6 @@ import com.gmail.nossr50.datatypes.skills.SubSkillType;
 import com.gmail.nossr50.locale.LocaleLoader;
 import com.gmail.nossr50.util.Permissions;
 import com.gmail.nossr50.util.skills.RankUtils;
-import com.gmail.nossr50.util.skills.SkillActivationType;
 import com.gmail.nossr50.util.text.TextComponentFactory;
 import net.kyori.adventure.text.Component;
 import org.bukkit.Material;
@@ -45,7 +44,7 @@ public class HerbalismCommand extends SkillCommand {
         
         // DOUBLE DROPS
         if (canDoubleDrop) {
-            String[] doubleDropStrings = getAbilityDisplayValues(SkillActivationType.RANDOM_LINEAR_100_SCALE_WITH_CAP, player, SubSkillType.HERBALISM_DOUBLE_DROPS);
+            String[] doubleDropStrings = getAbilityDisplayValues(SkillProbabilityType.DYNAMIC_CONFIGURABLE, player, SubSkillType.HERBALISM_DOUBLE_DROPS);
             doubleDropChance = doubleDropStrings[0];
             doubleDropChanceLucky = doubleDropStrings[1];
         }
@@ -66,21 +65,21 @@ public class HerbalismCommand extends SkillCommand {
         if (canGreenThumbBlocks || canGreenThumbPlants) {
             greenThumbStage = RankUtils.getRank(player, SubSkillType.HERBALISM_GREEN_THUMB);
 
-            String[] greenThumbStrings = getAbilityDisplayValues(SkillActivationType.RANDOM_LINEAR_100_SCALE_WITH_CAP, player, SubSkillType.HERBALISM_GREEN_THUMB);
+            String[] greenThumbStrings = getAbilityDisplayValues(SkillProbabilityType.DYNAMIC_CONFIGURABLE, player, SubSkillType.HERBALISM_GREEN_THUMB);
             greenThumbChance = greenThumbStrings[0];
             greenThumbChanceLucky = greenThumbStrings[1];
         }
 
         // HYLIAN LUCK
         if (hasHylianLuck) {
-            String[] hylianLuckStrings = getAbilityDisplayValues(SkillActivationType.RANDOM_LINEAR_100_SCALE_WITH_CAP, player, SubSkillType.HERBALISM_HYLIAN_LUCK);
+            String[] hylianLuckStrings = getAbilityDisplayValues(SkillProbabilityType.DYNAMIC_CONFIGURABLE, player, SubSkillType.HERBALISM_HYLIAN_LUCK);
             hylianLuckChance = hylianLuckStrings[0];
             hylianLuckChanceLucky = hylianLuckStrings[1];
         }
 
         // SHROOM THUMB
         if (canShroomThumb) {
-            String[] shroomThumbStrings = getAbilityDisplayValues(SkillActivationType.RANDOM_LINEAR_100_SCALE_WITH_CAP, player, SubSkillType.HERBALISM_SHROOM_THUMB);
+            String[] shroomThumbStrings = getAbilityDisplayValues(SkillProbabilityType.DYNAMIC_CONFIGURABLE, player, SubSkillType.HERBALISM_SHROOM_THUMB);
             shroomThumbChance = shroomThumbStrings[0];
             shroomThumbChanceLucky = shroomThumbStrings[1];
         }

+ 6 - 8
src/main/java/com/gmail/nossr50/commands/skills/MiningCommand.java

@@ -7,7 +7,6 @@ import com.gmail.nossr50.skills.mining.MiningManager;
 import com.gmail.nossr50.util.Permissions;
 import com.gmail.nossr50.util.player.UserManager;
 import com.gmail.nossr50.util.skills.RankUtils;
-import com.gmail.nossr50.util.skills.SkillActivationType;
 import com.gmail.nossr50.util.text.TextComponentFactory;
 import net.kyori.adventure.text.Component;
 import org.bukkit.entity.Player;
@@ -32,7 +31,7 @@ public class MiningCommand extends SkillCommand {
 
     private boolean canSuperBreaker;
     private boolean canDoubleDrop;
-    private boolean canTripleDrop;
+    private boolean canMotherLode;
     private boolean canBlast;
     private boolean canBiggerBombs;
     private boolean canDemoExpert;
@@ -56,16 +55,15 @@ public class MiningCommand extends SkillCommand {
         }
 
         // Mastery TRIPLE DROPS
-        if (canTripleDrop) {
-            String[] masteryTripleDropStrings = getAbilityDisplayValues(SkillActivationType.RANDOM_LINEAR_100_SCALE_WITH_CAP, player, SubSkillType.MINING_MOTHER_LODE);
+        if (canMotherLode) {
+            String[] masteryTripleDropStrings = getAbilityDisplayValues(SkillProbabilityType.DYNAMIC_CONFIGURABLE, player, SubSkillType.MINING_MOTHER_LODE);
             tripleDropChance = masteryTripleDropStrings[0];
             tripleDropChanceLucky = masteryTripleDropStrings[1];
         }
-
         
         // DOUBLE DROPS
         if (canDoubleDrop) {
-            String[] doubleDropStrings = getAbilityDisplayValues(SkillActivationType.RANDOM_LINEAR_100_SCALE_WITH_CAP, player, SubSkillType.MINING_DOUBLE_DROPS);
+            String[] doubleDropStrings = getAbilityDisplayValues(SkillProbabilityType.DYNAMIC_CONFIGURABLE, player, SubSkillType.MINING_DOUBLE_DROPS);
             doubleDropChance = doubleDropStrings[0];
             doubleDropChanceLucky = doubleDropStrings[1];
         }
@@ -84,7 +82,7 @@ public class MiningCommand extends SkillCommand {
         canBlast = RankUtils.hasUnlockedSubskill(player, SubSkillType.MINING_BLAST_MINING) && Permissions.remoteDetonation(player);
         canDemoExpert = RankUtils.hasUnlockedSubskill(player, SubSkillType.MINING_DEMOLITIONS_EXPERTISE) && Permissions.demolitionsExpertise(player);
         canDoubleDrop = Permissions.canUseSubSkill(player, SubSkillType.MINING_DOUBLE_DROPS);
-        canTripleDrop = Permissions.canUseSubSkill(player, SubSkillType.MINING_MOTHER_LODE);
+        canMotherLode = Permissions.canUseSubSkill(player, SubSkillType.MINING_MOTHER_LODE);
         canSuperBreaker = RankUtils.hasUnlockedSubskill(player, SubSkillType.MINING_SUPER_BREAKER) && Permissions.superBreaker(player);
     }
 
@@ -113,7 +111,7 @@ public class MiningCommand extends SkillCommand {
             //messages.add(LocaleLoader.getString("Mining.Effect.DropChance", doubleDropChance) + (isLucky ? LocaleLoader.getString("Perks.Lucky.Bonus", doubleDropChanceLucky) : ""));
         }
 
-        if(canTripleDrop) {
+        if(canMotherLode) {
             messages.add(getStatMessage(SubSkillType.MINING_MOTHER_LODE, tripleDropChance)
                     + (isLucky ? LocaleLoader.getString("Perks.Lucky.Bonus", tripleDropChanceLucky) : ""));
         }

+ 1 - 2
src/main/java/com/gmail/nossr50/commands/skills/RepairCommand.java

@@ -12,7 +12,6 @@ import com.gmail.nossr50.skills.repair.repairables.Repairable;
 import com.gmail.nossr50.util.Permissions;
 import com.gmail.nossr50.util.player.UserManager;
 import com.gmail.nossr50.util.skills.RankUtils;
-import com.gmail.nossr50.util.skills.SkillActivationType;
 import com.gmail.nossr50.util.text.TextComponentFactory;
 import net.kyori.adventure.text.Component;
 import org.bukkit.Material;
@@ -68,7 +67,7 @@ public class RepairCommand extends SkillCommand {
 
         // SUPER REPAIR
         if (canSuperRepair) {
-            String[] superRepairStrings = getAbilityDisplayValues(SkillActivationType.RANDOM_LINEAR_100_SCALE_WITH_CAP, player, SubSkillType.REPAIR_SUPER_REPAIR);
+            String[] superRepairStrings = getAbilityDisplayValues(SkillProbabilityType.DYNAMIC_CONFIGURABLE, player, SubSkillType.REPAIR_SUPER_REPAIR);
             superRepairChance = superRepairStrings[0];
             superRepairChanceLucky = superRepairStrings[1];
         }

+ 1 - 1
src/main/java/com/gmail/nossr50/commands/skills/SkillCommand.java

@@ -219,7 +219,7 @@ public abstract class SkillCommand implements TabExecutor {
         return Math.min((int) skillValue, maxLevel) / rankChangeLevel;
     }
 
-    protected String[] getAbilityDisplayValues(SkillActivationType skillActivationType, Player player, SubSkillType subSkill) {
+    protected static String[] getAbilityDisplayValues(SkillActivationType skillActivationType, Player player, SubSkillType subSkill) {
         return RandomChanceUtil.calculateAbilityDisplayValues(skillActivationType, player, subSkill);
     }
 

+ 2 - 3
src/main/java/com/gmail/nossr50/commands/skills/SmeltingCommand.java

@@ -6,7 +6,6 @@ import com.gmail.nossr50.locale.LocaleLoader;
 import com.gmail.nossr50.util.Permissions;
 import com.gmail.nossr50.util.player.UserManager;
 import com.gmail.nossr50.util.skills.RankUtils;
-import com.gmail.nossr50.util.skills.SkillActivationType;
 import com.gmail.nossr50.util.text.TextComponentFactory;
 import net.kyori.adventure.text.Component;
 import org.bukkit.entity.Player;
@@ -39,14 +38,14 @@ public class SmeltingCommand extends SkillCommand {
 
         // FLUX MINING
         /*if (canFluxMine) {
-            String[] fluxMiningStrings = getAbilityDisplayValues(SkillActivationType.RANDOM_LINEAR_100_SCALE_WITH_CAP, player, SubSkillType.SMELTING_FLUX_MINING);
+            String[] fluxMiningStrings = getAbilityDisplayValues(SkillProbabilityType.DYNAMIC_CONFIGURABLE, player, SubSkillType.SMELTING_FLUX_MINING);
             str_fluxMiningChance = fluxMiningStrings[0];
             str_fluxMiningChanceLucky = fluxMiningStrings[1];
         }*/
         
         // SECOND SMELT
         if (canSecondSmelt) {
-            String[] secondSmeltStrings = getAbilityDisplayValues(SkillActivationType.RANDOM_LINEAR_100_SCALE_WITH_CAP, player, SubSkillType.SMELTING_SECOND_SMELT);
+            String[] secondSmeltStrings = getAbilityDisplayValues(SkillProbabilityType.DYNAMIC_CONFIGURABLE, player, SubSkillType.SMELTING_SECOND_SMELT);
             str_secondSmeltChance = secondSmeltStrings[0];
             str_secondSmeltChanceLucky = secondSmeltStrings[1];
         }

+ 2 - 3
src/main/java/com/gmail/nossr50/commands/skills/SwordsCommand.java

@@ -8,7 +8,6 @@ import com.gmail.nossr50.util.Permissions;
 import com.gmail.nossr50.util.player.UserManager;
 import com.gmail.nossr50.util.skills.CombatUtils;
 import com.gmail.nossr50.util.skills.RankUtils;
-import com.gmail.nossr50.util.skills.SkillActivationType;
 import com.gmail.nossr50.util.text.TextComponentFactory;
 import net.kyori.adventure.text.Component;
 import org.bukkit.entity.Player;
@@ -37,7 +36,7 @@ public class SwordsCommand extends SkillCommand {
     protected void dataCalculations(Player player, float skillValue) {
         // SWORDS_COUNTER_ATTACK
         if (canCounter) {
-            String[] counterStrings = getAbilityDisplayValues(SkillActivationType.RANDOM_LINEAR_100_SCALE_WITH_CAP, player, SubSkillType.SWORDS_COUNTER_ATTACK);
+            String[] counterStrings = getAbilityDisplayValues(SkillProbabilityType.DYNAMIC_CONFIGURABLE, player, SubSkillType.SWORDS_COUNTER_ATTACK);
             counterChance = counterStrings[0];
             counterChanceLucky = counterStrings[1];
         }
@@ -46,7 +45,7 @@ public class SwordsCommand extends SkillCommand {
         if (canBleed) {
             bleedLength = UserManager.getPlayer(player).getSwordsManager().getRuptureBleedTicks();
 
-            String[] bleedStrings = getAbilityDisplayValues(SkillActivationType.RANDOM_LINEAR_100_SCALE_WITH_CAP, player, SubSkillType.SWORDS_RUPTURE);
+            String[] bleedStrings = getAbilityDisplayValues(SkillProbabilityType.DYNAMIC_CONFIGURABLE, player, SubSkillType.SWORDS_RUPTURE);
             bleedChance = bleedStrings[0];
             bleedChanceLucky = bleedStrings[1];
         }

+ 1 - 2
src/main/java/com/gmail/nossr50/commands/skills/TamingCommand.java

@@ -5,7 +5,6 @@ import com.gmail.nossr50.datatypes.skills.SubSkillType;
 import com.gmail.nossr50.locale.LocaleLoader;
 import com.gmail.nossr50.skills.taming.Taming;
 import com.gmail.nossr50.util.Permissions;
-import com.gmail.nossr50.util.skills.SkillActivationType;
 import com.gmail.nossr50.util.text.TextComponentFactory;
 import net.kyori.adventure.text.Component;
 import org.bukkit.entity.EntityType;
@@ -35,7 +34,7 @@ public class TamingCommand extends SkillCommand {
     @Override
     protected void dataCalculations(Player player, float skillValue) {
         if (canGore) {
-            String[] goreStrings = getAbilityDisplayValues(SkillActivationType.RANDOM_LINEAR_100_SCALE_WITH_CAP, player, SubSkillType.TAMING_GORE);
+            String[] goreStrings = getAbilityDisplayValues(SkillProbabilityType.DYNAMIC_CONFIGURABLE, player, SubSkillType.TAMING_GORE);
             goreChance = goreStrings[0];
             goreChanceLucky = goreStrings[1];
         }

+ 3 - 4
src/main/java/com/gmail/nossr50/commands/skills/UnarmedCommand.java

@@ -7,7 +7,6 @@ import com.gmail.nossr50.util.Permissions;
 import com.gmail.nossr50.util.player.UserManager;
 import com.gmail.nossr50.util.skills.CombatUtils;
 import com.gmail.nossr50.util.skills.RankUtils;
-import com.gmail.nossr50.util.skills.SkillActivationType;
 import com.gmail.nossr50.util.text.TextComponentFactory;
 import net.kyori.adventure.text.Component;
 import org.bukkit.entity.Player;
@@ -40,7 +39,7 @@ public class UnarmedCommand extends SkillCommand {
     protected void dataCalculations(Player player, float skillValue) {
         // UNARMED_ARROW_DEFLECT
         if (canDeflect) {
-            String[] deflectStrings = getAbilityDisplayValues(SkillActivationType.RANDOM_LINEAR_100_SCALE_WITH_CAP, player, SubSkillType.UNARMED_ARROW_DEFLECT);
+            String[] deflectStrings = getAbilityDisplayValues(SkillProbabilityType.DYNAMIC_CONFIGURABLE, player, SubSkillType.UNARMED_ARROW_DEFLECT);
             deflectChance = deflectStrings[0];
             deflectChanceLucky = deflectStrings[1];
         }
@@ -54,7 +53,7 @@ public class UnarmedCommand extends SkillCommand {
 
         // UNARMED_DISARM
         if (canDisarm) {
-            String[] disarmStrings = getAbilityDisplayValues(SkillActivationType.RANDOM_LINEAR_100_SCALE_WITH_CAP, player, SubSkillType.UNARMED_DISARM);
+            String[] disarmStrings = getAbilityDisplayValues(SkillProbabilityType.DYNAMIC_CONFIGURABLE, player, SubSkillType.UNARMED_DISARM);
             disarmChance = disarmStrings[0];
             disarmChanceLucky = disarmStrings[1];
         }
@@ -66,7 +65,7 @@ public class UnarmedCommand extends SkillCommand {
 
         // IRON GRIP
         if (canIronGrip) {
-            String[] ironGripStrings = getAbilityDisplayValues(SkillActivationType.RANDOM_LINEAR_100_SCALE_WITH_CAP, player, SubSkillType.UNARMED_IRON_GRIP);
+            String[] ironGripStrings = getAbilityDisplayValues(SkillProbabilityType.DYNAMIC_CONFIGURABLE, player, SubSkillType.UNARMED_IRON_GRIP);
             ironGripChance = ironGripStrings[0];
             ironGripChanceLucky = ironGripStrings[1];
         }

+ 2 - 3
src/main/java/com/gmail/nossr50/commands/skills/WoodcuttingCommand.java

@@ -5,7 +5,6 @@ import com.gmail.nossr50.datatypes.skills.SubSkillType;
 import com.gmail.nossr50.locale.LocaleLoader;
 import com.gmail.nossr50.util.Permissions;
 import com.gmail.nossr50.util.skills.RankUtils;
-import com.gmail.nossr50.util.skills.SkillActivationType;
 import com.gmail.nossr50.util.text.TextComponentFactory;
 import net.kyori.adventure.text.Component;
 import org.bukkit.entity.Player;
@@ -43,7 +42,7 @@ public class WoodcuttingCommand extends SkillCommand {
 
         //Clean Cuts
         if(canTripleDrop) {
-            String[] tripleDropStrings = getAbilityDisplayValues(SkillActivationType.RANDOM_LINEAR_100_SCALE_WITH_CAP, player, SubSkillType.WOODCUTTING_CLEAN_CUTS);
+            String[] tripleDropStrings = getAbilityDisplayValues(SkillProbabilityType.DYNAMIC_CONFIGURABLE, player, SubSkillType.WOODCUTTING_CLEAN_CUTS);
             tripleDropChance = tripleDropStrings[0];
             tripleDropChanceLucky = tripleDropStrings[1];
         }
@@ -57,7 +56,7 @@ public class WoodcuttingCommand extends SkillCommand {
     }
 
     private void setDoubleDropClassicChanceStrings(Player player) {
-        String[] doubleDropStrings = getAbilityDisplayValues(SkillActivationType.RANDOM_LINEAR_100_SCALE_WITH_CAP, player, SubSkillType.WOODCUTTING_HARVEST_LUMBER);
+        String[] doubleDropStrings = getAbilityDisplayValues(SkillProbabilityType.DYNAMIC_CONFIGURABLE, player, SubSkillType.WOODCUTTING_HARVEST_LUMBER);
         doubleDropChance = doubleDropStrings[0];
         doubleDropChanceLucky = doubleDropStrings[1];
     }

+ 16 - 17
src/main/java/com/gmail/nossr50/datatypes/skills/subskills/acrobatics/Roll.java

@@ -14,11 +14,10 @@ import com.gmail.nossr50.util.ItemUtils;
 import com.gmail.nossr50.util.Permissions;
 import com.gmail.nossr50.util.player.NotificationManager;
 import com.gmail.nossr50.util.player.UserManager;
-import com.gmail.nossr50.util.random.RandomChanceSkill;
 import com.gmail.nossr50.util.random.RandomChanceUtil;
+import com.gmail.nossr50.util.random.SkillProbabilityType;
 import com.gmail.nossr50.util.skills.PerksUtils;
 import com.gmail.nossr50.util.skills.RankUtils;
-import com.gmail.nossr50.util.skills.SkillActivationType;
 import com.gmail.nossr50.util.skills.SkillUtils;
 import com.gmail.nossr50.util.sounds.SoundManager;
 import com.gmail.nossr50.util.sounds.SoundType;
@@ -125,14 +124,14 @@ public class Roll extends AcrobaticsSubSkill {
         float skillValue = playerProfile.getSkillLevel(getPrimarySkill());
         boolean isLucky = Permissions.lucky(player, getPrimarySkill());
 
-        String[] rollStrings = RandomChanceUtil.calculateAbilityDisplayValues(SkillActivationType.RANDOM_LINEAR_100_SCALE_WITH_CAP, player, SubSkillType.ACROBATICS_ROLL);
+        String[] rollStrings = RandomChanceUtil.calculateAbilityDisplayValues(SkillProbabilityType.DYNAMIC_CONFIGURABLE, player, SubSkillType.ACROBATICS_ROLL);
         rollChance = rollStrings[0];
         rollChanceLucky = rollStrings[1];
 
         /*
          * Graceful is double the odds of a normal roll
          */
-        String[] gracefulRollStrings = RandomChanceUtil.calculateAbilityDisplayValuesCustom(SkillActivationType.RANDOM_LINEAR_100_SCALE_WITH_CAP, player, SubSkillType.ACROBATICS_ROLL, 2.0D);
+        String[] gracefulRollStrings = RandomChanceUtil.calculateAbilityDisplayValuesCustom(SkillProbabilityType.DYNAMIC_CONFIGURABLE, player, SubSkillType.ACROBATICS_ROLL, 2.0D);
         gracefulRollChance = gracefulRollStrings[0];
         gracefulRollChanceLucky = gracefulRollStrings[1];
 
@@ -199,7 +198,7 @@ public class Roll extends AcrobaticsSubSkill {
         double modifiedDamage = calculateModifiedRollDamage(damage, AdvancedConfig.getInstance().getRollDamageThreshold());
 
         if (!isFatal(player, modifiedDamage)
-                && RandomChanceUtil.isActivationSuccessful(SkillActivationType.RANDOM_LINEAR_100_SCALE_WITH_CAP, SubSkillType.ACROBATICS_ROLL, player)) {
+                && SkillUtils.isSkillRNGSuccessful(SkillProbabilityType.DYNAMIC_CONFIGURABLE, SubSkillType.ACROBATICS_ROLL, player)) {
             NotificationManager.sendPlayerInformation(player, NotificationType.SUBSKILL_MESSAGE, "Acrobatics.Roll.Text");
             SoundManager.sendCategorizedSound(player, player.getLocation(), SoundType.ROLL_ACTIVATED, SoundCategory.PLAYERS);
             //player.sendMessage(LocaleLoader.getString("Acrobatics.Roll.Text"));
@@ -236,11 +235,11 @@ public class Roll extends AcrobaticsSubSkill {
     private double gracefulRollCheck(Player player, McMMOPlayer mcMMOPlayer, double damage, int skillLevel) {
         double modifiedDamage = calculateModifiedRollDamage(damage, AdvancedConfig.getInstance().getRollDamageThreshold() * 2);
 
-        RandomChanceSkill rcs = new RandomChanceSkill(player, subSkillType);
-        rcs.setSkillLevel(rcs.getSkillLevel() * 2); //Double the effective odds
+        SkillProbabilityWrapper rcs = new SkillProbabilityWrapper(player, subSkillType);
+        rcs.setxPos(rcs.getxPos() * 2); //Double the effective odds
 
         if (!isFatal(player, modifiedDamage)
-                && RandomChanceUtil.checkRandomChanceExecutionSuccess(rcs))
+                && RandomChanceUtil.processProbabilityResults(rcs))
         {
             NotificationManager.sendPlayerInformation(player, NotificationType.SUBSKILL_MESSAGE, "Acrobatics.Ability.Proc");
             SoundManager.sendCategorizedSound(player, player.getLocation(), SoundType.ROLL_ACTIVATED, SoundCategory.PLAYERS,0.5F);
@@ -373,17 +372,17 @@ public class Roll extends AcrobaticsSubSkill {
         double rollChanceHalfMax, graceChanceHalfMax, damageThreshold, chancePerLevel;
 
         //Chance to roll at half max skill
-        RandomChanceSkill rollHalfMaxSkill = new RandomChanceSkill(null, subSkillType);
+        SkillProbabilityWrapper rollHalfMaxSkill = new SkillProbabilityWrapper(null, subSkillType);
         int halfMaxSkillValue = AdvancedConfig.getInstance().getMaxBonusLevel(SubSkillType.ACROBATICS_ROLL)/2;
-        rollHalfMaxSkill.setSkillLevel(halfMaxSkillValue);
+        rollHalfMaxSkill.setxPos(halfMaxSkillValue);
 
         //Chance to graceful roll at full skill
-        RandomChanceSkill rollGraceHalfMaxSkill = new RandomChanceSkill(null, subSkillType);
-        rollGraceHalfMaxSkill.setSkillLevel(halfMaxSkillValue * 2); //Double the effective odds
+        SkillProbabilityWrapper rollGraceHalfMaxSkill = new SkillProbabilityWrapper(null, subSkillType);
+        rollGraceHalfMaxSkill.setxPos(halfMaxSkillValue * 2); //Double the effective odds
 
         //Chance to roll per level
-        RandomChanceSkill rollOneSkillLevel = new RandomChanceSkill(null, subSkillType);
-        rollGraceHalfMaxSkill.setSkillLevel(1); //Level 1 skill
+        SkillProbabilityWrapper rollOneSkillLevel = new SkillProbabilityWrapper(null, subSkillType);
+        rollGraceHalfMaxSkill.setxPos(1); //Level 1 skill
 
         //Chance Stat Calculations
         rollChanceHalfMax       = RandomChanceUtil.getRandomChanceExecutionChance(rollHalfMaxSkill);
@@ -408,10 +407,10 @@ public class Roll extends AcrobaticsSubSkill {
     {
         double playerChanceRoll, playerChanceGrace;
 
-        RandomChanceSkill roll          = new RandomChanceSkill(player, getSubSkillType());
-        RandomChanceSkill graceful      = new RandomChanceSkill(player, getSubSkillType());
+        SkillProbabilityWrapper roll          = new SkillProbabilityWrapper(player, getSubSkillType());
+        SkillProbabilityWrapper graceful      = new SkillProbabilityWrapper(player, getSubSkillType());
 
-        graceful.setSkillLevel(graceful.getSkillLevel() * 2); //Double odds
+        graceful.setxPos(graceful.getxPos() * 2); //Double odds
 
         //Calculate
         playerChanceRoll        = RandomChanceUtil.getRandomChanceExecutionChance(roll);

+ 40 - 4
src/main/java/com/gmail/nossr50/datatypes/treasure/Treasure.java

@@ -1,26 +1,37 @@
 package com.gmail.nossr50.datatypes.treasure;
 
 import com.gmail.nossr50.config.Config;
+import com.gmail.nossr50.util.random.Probability;
+import com.gmail.nossr50.util.random.ProbabilityFactory;
+import com.gmail.nossr50.util.random.ProbabilityImpl;
+import com.google.common.base.Objects;
 import org.bukkit.inventory.ItemStack;
+import org.jetbrains.annotations.NotNull;
 
 public abstract class Treasure {
     private int xp;
     private double dropChance;
+    private @NotNull Probability dropProbability;
     private int dropLevel;
-    private ItemStack drop;
+    private @NotNull ItemStack drop;
 
     public Treasure(ItemStack drop, int xp, double dropChance, int dropLevel) {
         this.drop = drop;
         this.xp = xp;
         this.dropChance = dropChance;
+        this.dropProbability = new ProbabilityImpl(ProbabilityFactory.probabilityFromPercent(dropChance));
         this.dropLevel = dropLevel;
     }
 
-    public ItemStack getDrop() {
+    public @NotNull Probability getDropProbability() {
+        return dropProbability;
+    }
+
+    public @NotNull ItemStack getDrop() {
         return drop;
     }
 
-    public void setDrop(ItemStack drop) {
+    public void setDrop(@NotNull ItemStack drop) {
         this.drop = drop;
     }
 
@@ -36,8 +47,9 @@ public abstract class Treasure {
         return dropChance;
     }
 
-    public void setDropChance(Double dropChance) {
+    public void setDropChance(double dropChance) {
         this.dropChance = dropChance;
+        this.dropProbability = new ProbabilityImpl(ProbabilityFactory.probabilityFromPercent(dropChance));
     }
 
     public int getDropLevel() {
@@ -51,4 +63,28 @@ public abstract class Treasure {
     public void setDropLevel(int dropLevel) {
         this.dropLevel = dropLevel;
     }
+
+    @Override
+    public String toString() {
+        return "Treasure{" +
+                "xp=" + xp +
+                ", dropChance=" + dropChance +
+                ", dropProbability=" + dropProbability +
+                ", dropLevel=" + dropLevel +
+                ", drop=" + drop +
+                '}';
+    }
+
+    @Override
+    public boolean equals(Object o) {
+        if (this == o) return true;
+        if (o == null || getClass() != o.getClass()) return false;
+        Treasure treasure = (Treasure) o;
+        return xp == treasure.xp && Double.compare(treasure.dropChance, dropChance) == 0 && dropLevel == treasure.dropLevel && Objects.equal(dropProbability, treasure.dropProbability) && Objects.equal(drop, treasure.drop);
+    }
+
+    @Override
+    public int hashCode() {
+        return Objects.hashCode(xp, dropChance, dropProbability, dropLevel, drop);
+    }
 }

+ 2 - 3
src/main/java/com/gmail/nossr50/listeners/EntityListener.java

@@ -25,9 +25,8 @@ import com.gmail.nossr50.util.compat.layers.persistentdata.AbstractPersistentDat
 import com.gmail.nossr50.util.compat.layers.persistentdata.MobMetaFlagType;
 import com.gmail.nossr50.util.player.NotificationManager;
 import com.gmail.nossr50.util.player.UserManager;
-import com.gmail.nossr50.util.random.RandomChanceUtil;
 import com.gmail.nossr50.util.skills.CombatUtils;
-import com.gmail.nossr50.util.skills.SkillActivationType;
+import com.gmail.nossr50.util.skills.SkillUtils;
 import com.gmail.nossr50.worldguard.WorldGuardManager;
 import com.gmail.nossr50.worldguard.WorldGuardUtils;
 import org.bukkit.Material;
@@ -180,7 +179,7 @@ public class EntityListener implements Listener {
                     }
                 }
 
-                if (RandomChanceUtil.isActivationSuccessful(SkillActivationType.RANDOM_LINEAR_100_SCALE_WITH_CAP, SubSkillType.ARCHERY_ARROW_RETRIEVAL, player)) {
+                if (SkillUtils.isSkillRNGSuccessful(SkillProbabilityType.DYNAMIC_CONFIGURABLE, SubSkillType.ARCHERY_ARROW_RETRIEVAL, player)) {
                     projectile.setMetadata(mcMMO.trackedArrow, mcMMO.metadataValue);
                 }
             }

+ 1 - 3
src/main/java/com/gmail/nossr50/skills/acrobatics/AcrobaticsManager.java

@@ -12,10 +12,8 @@ import com.gmail.nossr50.skills.SkillManager;
 import com.gmail.nossr50.util.Misc;
 import com.gmail.nossr50.util.Permissions;
 import com.gmail.nossr50.util.player.NotificationManager;
-import com.gmail.nossr50.util.random.RandomChanceUtil;
 import com.gmail.nossr50.util.skills.ParticleEffectUtils;
 import com.gmail.nossr50.util.skills.RankUtils;
-import com.gmail.nossr50.util.skills.SkillActivationType;
 import com.gmail.nossr50.util.skills.SkillUtils;
 import org.bukkit.Location;
 import org.bukkit.entity.Entity;
@@ -88,7 +86,7 @@ public class AcrobaticsManager extends SkillManager {
         double modifiedDamage = Acrobatics.calculateModifiedDodgeDamage(damage, Acrobatics.dodgeDamageModifier);
         Player player = getPlayer();
 
-        if (!isFatal(modifiedDamage) && RandomChanceUtil.isActivationSuccessful(SkillActivationType.RANDOM_LINEAR_100_SCALE_WITH_CAP, SubSkillType.ACROBATICS_DODGE, player)) {
+        if (!isFatal(modifiedDamage) && SkillUtils.isSkillRNGSuccessful(SkillProbabilityType.DYNAMIC_CONFIGURABLE, SubSkillType.ACROBATICS_DODGE, player)) {
             ParticleEffectUtils.playDodgeEffect(player);
 
             if (mmoPlayer.useChatNotifications()) {

+ 3 - 3
src/main/java/com/gmail/nossr50/skills/archery/ArcheryManager.java

@@ -9,9 +9,9 @@ import com.gmail.nossr50.skills.SkillManager;
 import com.gmail.nossr50.util.Misc;
 import com.gmail.nossr50.util.Permissions;
 import com.gmail.nossr50.util.player.NotificationManager;
-import com.gmail.nossr50.util.random.RandomChanceUtil;
 import com.gmail.nossr50.util.skills.RankUtils;
 import com.gmail.nossr50.util.skills.SkillActivationType;
+import com.gmail.nossr50.util.skills.SkillUtils;
 import org.bukkit.Location;
 import org.bukkit.entity.Entity;
 import org.bukkit.entity.LivingEntity;
@@ -88,7 +88,7 @@ public class ArcheryManager extends SkillManager {
      * @param defender The {@link Player} being affected by the ability
      */
     public double daze(Player defender) {
-        if (!RandomChanceUtil.isActivationSuccessful(SkillActivationType.RANDOM_LINEAR_100_SCALE_WITH_CAP, SubSkillType.ARCHERY_DAZE, getPlayer())) {
+        if (!SkillUtils.isSkillRNGSuccessful(SkillProbabilityType.DYNAMIC_CONFIGURABLE, SubSkillType.ARCHERY_DAZE, getPlayer())) {
             return 0;
         }
 
@@ -116,7 +116,7 @@ public class ArcheryManager extends SkillManager {
      * @param oldDamage The raw damage value of this arrow before we modify it
      */
     public double skillShot(double oldDamage) {
-        if (!RandomChanceUtil.isActivationSuccessful(SkillActivationType.ALWAYS_FIRES, SubSkillType.ARCHERY_SKILL_SHOT, getPlayer())) {
+        if (!SkillUtils.isSkillRNGSuccessful(SkillActivationType.ALWAYS_FIRES, SubSkillType.ARCHERY_SKILL_SHOT, getPlayer())) {
             return oldDamage;
         }
 

+ 5 - 5
src/main/java/com/gmail/nossr50/skills/axes/AxesManager.java

@@ -11,7 +11,7 @@ import com.gmail.nossr50.skills.SkillManager;
 import com.gmail.nossr50.util.ItemUtils;
 import com.gmail.nossr50.util.Permissions;
 import com.gmail.nossr50.util.player.NotificationManager;
-import com.gmail.nossr50.util.random.RandomChanceUtil;
+import com.gmail.nossr50.util.random.SkillProbabilityType;
 import com.gmail.nossr50.util.skills.*;
 import org.bukkit.entity.LivingEntity;
 import org.bukkit.entity.Player;
@@ -69,7 +69,7 @@ public class AxesManager extends SkillManager {
      * Handle the effects of the Axe Mastery ability
      */
     public double axeMastery() {
-        if (!RandomChanceUtil.isActivationSuccessful(SkillActivationType.ALWAYS_FIRES, SubSkillType.AXES_AXE_MASTERY, getPlayer())) {
+        if (!SkillUtils.isSkillRNGSuccessful(SkillActivationType.ALWAYS_FIRES, SubSkillType.AXES_AXE_MASTERY, getPlayer())) {
             return 0;
         }
 
@@ -83,7 +83,7 @@ public class AxesManager extends SkillManager {
      * @param damage The amount of damage initially dealt by the event
      */
     public double criticalHit(LivingEntity target, double damage) {
-        if (!RandomChanceUtil.isActivationSuccessful(SkillActivationType.RANDOM_LINEAR_100_SCALE_WITH_CAP, SubSkillType.AXES_CRITICAL_STRIKES, getPlayer())) {
+        if (!SkillUtils.isSkillRNGSuccessful(SkillProbabilityType.DYNAMIC_CONFIGURABLE, SubSkillType.AXES_CRITICAL_STRIKES, getPlayer())) {
             return 0;
         }
 
@@ -119,7 +119,7 @@ public class AxesManager extends SkillManager {
 
         for (ItemStack armor : target.getEquipment().getArmorContents()) {
             if (armor != null && ItemUtils.isArmor(armor)) {
-                if (RandomChanceUtil.isActivationSuccessful(SkillActivationType.RANDOM_STATIC_CHANCE, SubSkillType.AXES_ARMOR_IMPACT, getPlayer())) {
+                if (SkillUtils.isSkillRNGSuccessful(SkillProbabilityType.DYNAMIC_CONFIGURABLE, SubSkillType.AXES_ARMOR_IMPACT, getPlayer())) {
                     SkillUtils.handleDurabilityChange(armor, durabilityDamage, 1);
                 }
             }
@@ -137,7 +137,7 @@ public class AxesManager extends SkillManager {
      */
     public double greaterImpact(@NotNull LivingEntity target) {
         //static chance (3rd param)
-        if (!RandomChanceUtil.isActivationSuccessful(SkillActivationType.RANDOM_STATIC_CHANCE, SubSkillType.AXES_GREATER_IMPACT, getPlayer())) {
+        if (!SkillUtils.isSkillRNGSuccessful(SkillProbabilityType.DYNAMIC_CONFIGURABLE, SubSkillType.AXES_GREATER_IMPACT, getPlayer())) {
             return 0;
         }
 

+ 1 - 1
src/main/java/com/gmail/nossr50/skills/excavation/ExcavationManager.java

@@ -41,7 +41,7 @@ public class ExcavationManager extends SkillManager {
 
                 for (ExcavationTreasure treasure : treasures) {
                     if (skillLevel >= treasure.getDropLevel()
-                            && RandomChanceUtil.checkRandomChanceExecutionSuccess(getPlayer(), PrimarySkillType.EXCAVATION, treasure.getDropChance())) {
+                            && SkillUtils.isStaticSkillRNGSuccessful(PrimarySkillType.EXCAVATION, getPlayer(), treasure.getDropProbability())) {
 
                         //Spawn Vanilla XP orbs if a dice roll succeeds
                         if(RandomChanceUtil.rollDice(getArchaelogyExperienceOrbChance(), 100)) {

+ 7 - 6
src/main/java/com/gmail/nossr50/skills/fishing/FishingManager.java

@@ -20,8 +20,6 @@ import com.gmail.nossr50.skills.SkillManager;
 import com.gmail.nossr50.util.*;
 import com.gmail.nossr50.util.compat.layers.skills.MasterAnglerCompatibilityLayer;
 import com.gmail.nossr50.util.player.NotificationManager;
-import com.gmail.nossr50.util.random.RandomChanceSkillStatic;
-import com.gmail.nossr50.util.random.RandomChanceUtil;
 import com.gmail.nossr50.util.skills.CombatUtils;
 import com.gmail.nossr50.util.skills.RankUtils;
 import com.gmail.nossr50.util.skills.SkillUtils;
@@ -63,11 +61,14 @@ public class FishingManager extends SkillManager {
     }
 
     public boolean canShake(Entity target) {
-        return target instanceof LivingEntity && RankUtils.hasUnlockedSubskill(getPlayer(), SubSkillType.FISHING_SHAKE) && Permissions.isSubSkillEnabled(getPlayer(), SubSkillType.FISHING_SHAKE);
+        return target instanceof LivingEntity && RankUtils.hasUnlockedSubskill(getPlayer(), SubSkillType.FISHING_SHAKE)
+                && Permissions.isSubSkillEnabled(getPlayer(), SubSkillType.FISHING_SHAKE);
     }
 
     public boolean canMasterAngler() {
-        return mcMMO.getCompatibilityManager().getMasterAnglerCompatibilityLayer() != null && getSkillLevel() >= RankUtils.getUnlockLevel(SubSkillType.FISHING_MASTER_ANGLER) && Permissions.isSubSkillEnabled(getPlayer(), SubSkillType.FISHING_MASTER_ANGLER);
+        return mcMMO.getCompatibilityManager().getMasterAnglerCompatibilityLayer() != null
+                && getSkillLevel() >= RankUtils.getUnlockLevel(SubSkillType.FISHING_MASTER_ANGLER)
+                && Permissions.isSubSkillEnabled(getPlayer(), SubSkillType.FISHING_MASTER_ANGLER);
     }
 
 //    public void setFishingRodCastTimestamp()
@@ -477,8 +478,8 @@ public class FishingManager extends SkillManager {
      *
      * @param target The {@link LivingEntity} affected by the ability
      */
-    public void shakeCheck(LivingEntity target) {
-        if (RandomChanceUtil.checkRandomChanceExecutionSuccess(new RandomChanceSkillStatic(getShakeChance(), getPlayer(), SubSkillType.FISHING_SHAKE))) {
+    public void shakeCheck(@NotNull LivingEntity target) {
+        if (SkillUtils.isStaticSkillRNGSuccessful(PrimarySkillType.FISHING, getPlayer(), getShakeChance())) {
             List<ShakeTreasure> possibleDrops = Fishing.findPossibleDrops(target);
 
             if (possibleDrops == null || possibleDrops.isEmpty()) {

+ 4 - 6
src/main/java/com/gmail/nossr50/skills/herbalism/HerbalismManager.java

@@ -21,10 +21,8 @@ import com.gmail.nossr50.runnables.skills.DelayedHerbalismXPCheckTask;
 import com.gmail.nossr50.skills.SkillManager;
 import com.gmail.nossr50.util.*;
 import com.gmail.nossr50.util.player.NotificationManager;
-import com.gmail.nossr50.util.random.RandomChanceSkillStatic;
 import com.gmail.nossr50.util.random.RandomChanceUtil;
 import com.gmail.nossr50.util.skills.RankUtils;
-import com.gmail.nossr50.util.skills.SkillActivationType;
 import com.gmail.nossr50.util.skills.SkillUtils;
 import com.gmail.nossr50.util.sounds.SoundManager;
 import com.gmail.nossr50.util.sounds.SoundType;
@@ -589,7 +587,7 @@ public class HerbalismManager extends SkillManager {
      * @return true if the ability was successful, false otherwise
      */
     public boolean processGreenThumbBlocks(BlockState blockState) {
-        if (!RandomChanceUtil.isActivationSuccessful(SkillActivationType.RANDOM_LINEAR_100_SCALE_WITH_CAP, SubSkillType.HERBALISM_GREEN_THUMB, getPlayer())) {
+        if (!SkillUtils.isSkillRNGSuccessful(SkillProbabilityType.DYNAMIC_CONFIGURABLE, SubSkillType.HERBALISM_GREEN_THUMB, getPlayer())) {
             NotificationManager.sendPlayerInformation(getPlayer(), NotificationType.SUBSKILL_MESSAGE_FAILED, "Herbalism.Ability.GTh.Fail");
             return false;
         }
@@ -604,7 +602,7 @@ public class HerbalismManager extends SkillManager {
      * @return true if the ability was successful, false otherwise
      */
     public boolean processHylianLuck(BlockState blockState) {
-        if (!RandomChanceUtil.isActivationSuccessful(SkillActivationType.RANDOM_LINEAR_100_SCALE_WITH_CAP, SubSkillType.HERBALISM_HYLIAN_LUCK, getPlayer())) {
+        if (!SkillUtils.isSkillRNGSuccessful(SkillProbabilityType.DYNAMIC_CONFIGURABLE, SubSkillType.HERBALISM_HYLIAN_LUCK, getPlayer())) {
             return false;
         }
 
@@ -623,7 +621,7 @@ public class HerbalismManager extends SkillManager {
 
         for (HylianTreasure treasure : treasures) {
             if (skillLevel >= treasure.getDropLevel()
-                    && RandomChanceUtil.checkRandomChanceExecutionSuccess(new RandomChanceSkillStatic(treasure.getDropChance(), getPlayer(), SubSkillType.HERBALISM_HYLIAN_LUCK))) {
+                    && RandomChanceUtil.processProbabilityResults(new SkillProbabilityWrapperStatic(treasure.getDropChance(), getPlayer(), SubSkillType.HERBALISM_HYLIAN_LUCK))) {
                 if (!EventUtils.simulateBlockBreak(blockState.getBlock(), player, false)) {
                     return false;
                 }
@@ -660,7 +658,7 @@ public class HerbalismManager extends SkillManager {
         playerInventory.removeItem(new ItemStack(Material.RED_MUSHROOM));
         player.updateInventory();
 
-        if (!RandomChanceUtil.isActivationSuccessful(SkillActivationType.RANDOM_LINEAR_100_SCALE_WITH_CAP, SubSkillType.HERBALISM_SHROOM_THUMB, player)) {
+        if (!SkillUtils.isSkillRNGSuccessful(SkillProbabilityType.DYNAMIC_CONFIGURABLE, SubSkillType.HERBALISM_SHROOM_THUMB, player)) {
             NotificationManager.sendPlayerInformation(player, NotificationType.SUBSKILL_MESSAGE_FAILED, "Herbalism.Ability.ShroomThumb.Fail");
             return false;
         }

+ 3 - 5
src/main/java/com/gmail/nossr50/skills/repair/RepairManager.java

@@ -16,10 +16,8 @@ import com.gmail.nossr50.util.EventUtils;
 import com.gmail.nossr50.util.Misc;
 import com.gmail.nossr50.util.Permissions;
 import com.gmail.nossr50.util.player.NotificationManager;
-import com.gmail.nossr50.util.random.RandomChanceSkillStatic;
 import com.gmail.nossr50.util.random.RandomChanceUtil;
 import com.gmail.nossr50.util.skills.RankUtils;
-import com.gmail.nossr50.util.skills.SkillActivationType;
 import com.gmail.nossr50.util.skills.SkillUtils;
 import com.gmail.nossr50.util.sounds.SoundManager;
 import com.gmail.nossr50.util.sounds.SoundType;
@@ -291,7 +289,7 @@ public class RepairManager extends SkillManager {
         if(!RankUtils.hasUnlockedSubskill(getPlayer(), SubSkillType.REPAIR_SUPER_REPAIR))
             return false;
 
-        if (RandomChanceUtil.isActivationSuccessful(SkillActivationType.RANDOM_LINEAR_100_SCALE_WITH_CAP, SubSkillType.REPAIR_SUPER_REPAIR, getPlayer())) {
+        if (SkillUtils.isSkillRNGSuccessful(SkillProbabilityType.DYNAMIC_CONFIGURABLE, SubSkillType.REPAIR_SUPER_REPAIR, getPlayer())) {
             NotificationManager.sendPlayerInformation(getPlayer(), NotificationType.SUBSKILL_MESSAGE, "Repair.Skills.FeltEasy");
             return true;
         }
@@ -342,10 +340,10 @@ public class RepairManager extends SkillManager {
 
             Enchantment enchantment = enchant.getKey();
 
-            if (RandomChanceUtil.checkRandomChanceExecutionSuccess(new RandomChanceSkillStatic(getKeepEnchantChance(), getPlayer(), SubSkillType.REPAIR_ARCANE_FORGING))) {
+            if (RandomChanceUtil.processProbabilityResults(new SkillProbabilityWrapperStatic(getKeepEnchantChance(), getPlayer(), SubSkillType.REPAIR_ARCANE_FORGING))) {
 
                 if (ArcaneForging.arcaneForgingDowngrades && enchantLevel > 1
-                        && (!RandomChanceUtil.checkRandomChanceExecutionSuccess(new RandomChanceSkillStatic(100 - getDowngradeEnchantChance(), getPlayer(), SubSkillType.REPAIR_ARCANE_FORGING)))) {
+                        && (!RandomChanceUtil.processProbabilityResults(new SkillProbabilityWrapperStatic(100 - getDowngradeEnchantChance(), getPlayer(), SubSkillType.REPAIR_ARCANE_FORGING)))) {
                     item.addUnsafeEnchantment(enchantment, enchantLevel - 1);
                     downgraded = true;
                 }

+ 3 - 4
src/main/java/com/gmail/nossr50/skills/salvage/SalvageManager.java

@@ -16,7 +16,6 @@ import com.gmail.nossr50.util.EventUtils;
 import com.gmail.nossr50.util.Misc;
 import com.gmail.nossr50.util.Permissions;
 import com.gmail.nossr50.util.player.NotificationManager;
-import com.gmail.nossr50.util.random.RandomChanceSkillStatic;
 import com.gmail.nossr50.util.random.RandomChanceUtil;
 import com.gmail.nossr50.util.skills.RankUtils;
 import com.gmail.nossr50.util.skills.SkillUtils;
@@ -123,7 +122,7 @@ public class SalvageManager extends SkillManager {
 
         for(int x = 0; x < potentialSalvageYield-1; x++) {
 
-            if(RandomChanceUtil.rollDice(chanceOfSuccess, 100)) {
+            if(RandomChanceUtil.rollDiceSimple(chanceOfSuccess, 100)) {
                 chanceOfSuccess-=3;
                 chanceOfSuccess = Math.max(chanceOfSuccess, 90);
 
@@ -253,12 +252,12 @@ public class SalvageManager extends SkillManager {
 
             if (!Salvage.arcaneSalvageEnchantLoss
                     || Permissions.hasSalvageEnchantBypassPerk(player)
-                    || RandomChanceUtil.checkRandomChanceExecutionSuccess(new RandomChanceSkillStatic(getExtractFullEnchantChance(), getPlayer(), SubSkillType.SALVAGE_ARCANE_SALVAGE))) {
+                    || RandomChanceUtil.processProbabilityResults(new SkillProbabilityWrapperStatic(getExtractFullEnchantChance(), getPlayer(), SubSkillType.SALVAGE_ARCANE_SALVAGE))) {
                 enchantMeta.addStoredEnchant(enchant.getKey(), enchantLevel, true);
             }
             else if (enchantLevel > 1
                     && Salvage.arcaneSalvageDowngrades
-                    && RandomChanceUtil.checkRandomChanceExecutionSuccess(new RandomChanceSkillStatic(getExtractPartialEnchantChance(), getPlayer(), SubSkillType.SALVAGE_ARCANE_SALVAGE))) {
+                    && RandomChanceUtil.processProbabilityResults(new SkillProbabilityWrapperStatic(getExtractPartialEnchantChance(), getPlayer(), SubSkillType.SALVAGE_ARCANE_SALVAGE))) {
                 enchantMeta.addStoredEnchant(enchant.getKey(), enchantLevel - 1, true);
                 downgraded = true;
             } else {

+ 2 - 3
src/main/java/com/gmail/nossr50/skills/smelting/SmeltingManager.java

@@ -8,9 +8,8 @@ import com.gmail.nossr50.datatypes.skills.PrimarySkillType;
 import com.gmail.nossr50.datatypes.skills.SubSkillType;
 import com.gmail.nossr50.skills.SkillManager;
 import com.gmail.nossr50.util.Permissions;
-import com.gmail.nossr50.util.random.RandomChanceUtil;
 import com.gmail.nossr50.util.skills.RankUtils;
-import com.gmail.nossr50.util.skills.SkillActivationType;
+import com.gmail.nossr50.util.skills.SkillUtils;
 import org.bukkit.event.inventory.FurnaceBurnEvent;
 import org.bukkit.inventory.ItemStack;
 
@@ -28,7 +27,7 @@ public class SmeltingManager extends SkillManager {
 
     public boolean isSecondSmeltSuccessful() {
         return Permissions.isSubSkillEnabled(getPlayer(), SubSkillType.SMELTING_SECOND_SMELT)
-                && RandomChanceUtil.isActivationSuccessful(SkillActivationType.RANDOM_LINEAR_100_SCALE_WITH_CAP, SubSkillType.SMELTING_SECOND_SMELT, getPlayer());
+                && SkillUtils.isSkillRNGSuccessful(SkillProbabilityType.DYNAMIC_CONFIGURABLE, SubSkillType.SMELTING_SECOND_SMELT, getPlayer());
     }
 
     /*

+ 3 - 4
src/main/java/com/gmail/nossr50/skills/swords/SwordsManager.java

@@ -11,10 +11,9 @@ import com.gmail.nossr50.skills.SkillManager;
 import com.gmail.nossr50.util.ItemUtils;
 import com.gmail.nossr50.util.Permissions;
 import com.gmail.nossr50.util.player.NotificationManager;
-import com.gmail.nossr50.util.random.RandomChanceUtil;
 import com.gmail.nossr50.util.skills.CombatUtils;
 import com.gmail.nossr50.util.skills.RankUtils;
-import com.gmail.nossr50.util.skills.SkillActivationType;
+import com.gmail.nossr50.util.skills.SkillUtils;
 import org.bukkit.entity.Entity;
 import org.bukkit.entity.LivingEntity;
 import org.bukkit.entity.Player;
@@ -62,7 +61,7 @@ public class SwordsManager extends SkillManager {
      */
     public void ruptureCheck(@NotNull LivingEntity target) throws IllegalStateException {
         if(BleedTimerTask.isBleedOperationAllowed()) {
-            if (RandomChanceUtil.isActivationSuccessful(SkillActivationType.RANDOM_LINEAR_100_SCALE_WITH_CAP, SubSkillType.SWORDS_RUPTURE, getPlayer())) {
+            if (SkillUtils.isSkillRNGSuccessful(SkillProbabilityType.DYNAMIC_CONFIGURABLE, SubSkillType.SWORDS_RUPTURE, getPlayer())) {
 
                 if (target instanceof Player) {
                     Player defender = (Player) target;
@@ -129,7 +128,7 @@ public class SwordsManager extends SkillManager {
      * @param damage The amount of damage initially dealt by the event
      */
     public void counterAttackChecks(@NotNull LivingEntity attacker, double damage) {
-        if (RandomChanceUtil.isActivationSuccessful(SkillActivationType.RANDOM_LINEAR_100_SCALE_WITH_CAP, SubSkillType.SWORDS_COUNTER_ATTACK, getPlayer())) {
+        if (SkillUtils.isSkillRNGSuccessful(SkillProbabilityType.DYNAMIC_CONFIGURABLE, SubSkillType.SWORDS_COUNTER_ATTACK, getPlayer())) {
             CombatUtils.dealDamage(attacker, damage / Swords.counterAttackModifier, getPlayer());
 
             NotificationManager.sendPlayerInformation(getPlayer(), NotificationType.SUBSKILL_MESSAGE, "Swords.Combat.Countered");

+ 4 - 5
src/main/java/com/gmail/nossr50/skills/taming/TamingManager.java

@@ -18,11 +18,10 @@ import com.gmail.nossr50.util.Misc;
 import com.gmail.nossr50.util.Permissions;
 import com.gmail.nossr50.util.compat.layers.persistentdata.MobMetaFlagType;
 import com.gmail.nossr50.util.player.NotificationManager;
-import com.gmail.nossr50.util.random.RandomChanceSkillStatic;
 import com.gmail.nossr50.util.random.RandomChanceUtil;
 import com.gmail.nossr50.util.skills.ParticleEffectUtils;
 import com.gmail.nossr50.util.skills.RankUtils;
-import com.gmail.nossr50.util.skills.SkillActivationType;
+import com.gmail.nossr50.util.skills.SkillUtils;
 import com.gmail.nossr50.util.sounds.SoundManager;
 import com.gmail.nossr50.util.sounds.SoundType;
 import com.gmail.nossr50.util.text.StringUtils;
@@ -147,7 +146,7 @@ public class TamingManager extends SkillManager {
      * @param damage The damage being absorbed by the wolf
      */
     public void fastFoodService(@NotNull Wolf wolf, double damage) {
-        if (!RandomChanceUtil.isActivationSuccessful(SkillActivationType.RANDOM_STATIC_CHANCE, SubSkillType.TAMING_FAST_FOOD_SERVICE, getPlayer())) {
+        if (!SkillUtils.isSkillRNGSuccessful(SkillProbabilityType.DYNAMIC_CONFIGURABLE, SubSkillType.TAMING_FAST_FOOD_SERVICE, getPlayer())) {
             return;
         }
 
@@ -168,7 +167,7 @@ public class TamingManager extends SkillManager {
      */
     public double gore(@NotNull LivingEntity target, double damage) {
         if(BleedTimerTask.isBleedOperationAllowed()) {
-            if (!RandomChanceUtil.isActivationSuccessful(SkillActivationType.RANDOM_LINEAR_100_SCALE_WITH_CAP, SubSkillType.TAMING_GORE, getPlayer())) {
+            if (!SkillUtils.isSkillRNGSuccessful(SkillProbabilityType.DYNAMIC_CONFIGURABLE, SubSkillType.TAMING_GORE, getPlayer())) {
                 return 0;
             }
 
@@ -276,7 +275,7 @@ public class TamingManager extends SkillManager {
         if(!RankUtils.hasUnlockedSubskill(getPlayer(), SubSkillType.TAMING_PUMMEL))
             return;
 
-        if(!RandomChanceUtil.checkRandomChanceExecutionSuccess(new RandomChanceSkillStatic(AdvancedConfig.getInstance().getPummelChance(), getPlayer(), SubSkillType.TAMING_PUMMEL)))
+        if(!RandomChanceUtil.processProbabilityResults(new SkillProbabilityWrapperStatic(AdvancedConfig.getInstance().getPummelChance(), getPlayer(), SubSkillType.TAMING_PUMMEL)))
             return;
 
         ParticleEffectUtils.playGreaterImpactEffect(target);

+ 6 - 6
src/main/java/com/gmail/nossr50/skills/unarmed/UnarmedManager.java

@@ -16,9 +16,9 @@ import com.gmail.nossr50.util.Misc;
 import com.gmail.nossr50.util.Permissions;
 import com.gmail.nossr50.util.player.NotificationManager;
 import com.gmail.nossr50.util.player.UserManager;
-import com.gmail.nossr50.util.random.RandomChanceUtil;
 import com.gmail.nossr50.util.skills.RankUtils;
 import com.gmail.nossr50.util.skills.SkillActivationType;
+import com.gmail.nossr50.util.skills.SkillUtils;
 import org.bukkit.Material;
 import org.bukkit.block.BlockState;
 import org.bukkit.entity.Item;
@@ -72,7 +72,7 @@ public class UnarmedManager extends SkillManager {
     }
 
     public boolean blockCrackerCheck(@NotNull BlockState blockState) {
-        if (!RandomChanceUtil.isActivationSuccessful(SkillActivationType.ALWAYS_FIRES, SubSkillType.UNARMED_BLOCK_CRACKER, getPlayer())) {
+        if (!SkillUtils.isSkillRNGSuccessful(SkillActivationType.ALWAYS_FIRES, SubSkillType.UNARMED_BLOCK_CRACKER, getPlayer())) {
             return false;
         }
 
@@ -103,7 +103,7 @@ public class UnarmedManager extends SkillManager {
      * @param defender The defending player
      */
     public void disarmCheck(@NotNull Player defender) {
-        if (RandomChanceUtil.isActivationSuccessful(SkillActivationType.RANDOM_LINEAR_100_SCALE_WITH_CAP, SubSkillType.UNARMED_DISARM, getPlayer()) && !hasIronGrip(defender)) {
+        if (SkillUtils.isSkillRNGSuccessful(SkillProbabilityType.DYNAMIC_CONFIGURABLE, SubSkillType.UNARMED_DISARM, getPlayer()) && !hasIronGrip(defender)) {
             if (EventUtils.callDisarmEvent(defender).isCancelled()) {
                 return;
             }
@@ -126,7 +126,7 @@ public class UnarmedManager extends SkillManager {
      * Check for arrow deflection.
      */
     public boolean deflectCheck() {
-        if (RandomChanceUtil.isActivationSuccessful(SkillActivationType.RANDOM_LINEAR_100_SCALE_WITH_CAP, SubSkillType.UNARMED_ARROW_DEFLECT, getPlayer())) {
+        if (SkillUtils.isSkillRNGSuccessful(SkillProbabilityType.DYNAMIC_CONFIGURABLE, SubSkillType.UNARMED_ARROW_DEFLECT, getPlayer())) {
             NotificationManager.sendPlayerInformation(getPlayer(), NotificationType.SUBSKILL_MESSAGE, "Combat.ArrowDeflect");
             return true;
         }
@@ -149,7 +149,7 @@ public class UnarmedManager extends SkillManager {
      * Handle the effects of the Iron Arm ability
      */
     public double calculateSteelArmStyleDamage() {
-        if (!RandomChanceUtil.isActivationSuccessful(SkillActivationType.ALWAYS_FIRES, SubSkillType.UNARMED_STEEL_ARM_STYLE, getPlayer())) {
+        if (!SkillUtils.isSkillRNGSuccessful(SkillActivationType.ALWAYS_FIRES, SubSkillType.UNARMED_STEEL_ARM_STYLE, getPlayer())) {
             return 0;
         }
 
@@ -183,7 +183,7 @@ public class UnarmedManager extends SkillManager {
     private boolean hasIronGrip(@NotNull Player defender) {
         if (!Misc.isNPCEntityExcludingVillagers(defender)
                 && Permissions.isSubSkillEnabled(defender, SubSkillType.UNARMED_IRON_GRIP)
-                && RandomChanceUtil.isActivationSuccessful(SkillActivationType.RANDOM_LINEAR_100_SCALE_WITH_CAP, SubSkillType.UNARMED_IRON_GRIP, defender)) {
+                && SkillUtils.isSkillRNGSuccessful(SkillProbabilityType.DYNAMIC_CONFIGURABLE, SubSkillType.UNARMED_IRON_GRIP, defender)) {
             NotificationManager.sendPlayerInformation(defender, NotificationType.SUBSKILL_MESSAGE, "Unarmed.Ability.IronGrip.Defender");
             NotificationManager.sendPlayerInformation(getPlayer(), NotificationType.SUBSKILL_MESSAGE, "Unarmed.Ability.IronGrip.Attacker");
 

+ 3 - 4
src/main/java/com/gmail/nossr50/skills/woodcutting/WoodcuttingManager.java

@@ -17,7 +17,6 @@ import com.gmail.nossr50.util.player.NotificationManager;
 import com.gmail.nossr50.util.random.RandomChanceUtil;
 import com.gmail.nossr50.util.skills.CombatUtils;
 import com.gmail.nossr50.util.skills.RankUtils;
-import com.gmail.nossr50.util.skills.SkillActivationType;
 import com.gmail.nossr50.util.skills.SkillUtils;
 import org.bukkit.Bukkit;
 import org.bukkit.Material;
@@ -70,11 +69,11 @@ public class WoodcuttingManager extends SkillManager {
     }
 
     private boolean checkHarvestLumberActivation() {
-        return RandomChanceUtil.isActivationSuccessful(SkillActivationType.RANDOM_LINEAR_100_SCALE_WITH_CAP, SubSkillType.WOODCUTTING_HARVEST_LUMBER, getPlayer());
+        return SkillUtils.isSkillRNGSuccessful(SkillProbabilityType.DYNAMIC_CONFIGURABLE, SubSkillType.WOODCUTTING_HARVEST_LUMBER, getPlayer());
     }
 
     private boolean checkCleanCutsActivation() {
-        return RandomChanceUtil.isActivationSuccessful(SkillActivationType.RANDOM_LINEAR_100_SCALE_WITH_CAP, SubSkillType.WOODCUTTING_CLEAN_CUTS, getPlayer());
+        return SkillUtils.isSkillRNGSuccessful(SkillProbabilityType.DYNAMIC_CONFIGURABLE, SubSkillType.WOODCUTTING_CLEAN_CUTS, getPlayer());
     }
 
     /**
@@ -328,7 +327,7 @@ public class WoodcuttingManager extends SkillManager {
 
                     if(RankUtils.hasReachedRank(2, player, SubSkillType.WOODCUTTING_KNOCK_ON_WOOD)) {
                         if(AdvancedConfig.getInstance().isKnockOnWoodXPOrbEnabled()) {
-                            if(RandomChanceUtil.rollDice(10, 100)) {
+                            if(RandomChanceUtil.rollDiceSimple(10, 100)) {
                                 int randOrbCount = Math.max(1, Misc.getRandom().nextInt(100));
                                 Misc.spawnExperienceOrb(blockState.getLocation(), randOrbCount);
                             }

+ 1 - 2
src/main/java/com/gmail/nossr50/util/BlockUtils.java

@@ -8,7 +8,6 @@ import com.gmail.nossr50.datatypes.skills.SubSkillType;
 import com.gmail.nossr50.mcMMO;
 import com.gmail.nossr50.skills.repair.Repair;
 import com.gmail.nossr50.skills.salvage.Salvage;
-import com.gmail.nossr50.util.random.RandomChanceSkill;
 import com.gmail.nossr50.util.random.RandomChanceUtil;
 import org.bukkit.Material;
 import org.bukkit.block.Block;
@@ -55,7 +54,7 @@ public final class BlockUtils {
      */
     public static boolean checkDoubleDrops(Player player, BlockState blockState, PrimarySkillType skillType, SubSkillType subSkillType) {
         if (Config.getInstance().getDoubleDropsEnabled(skillType, blockState.getType()) && Permissions.isSubSkillEnabled(player, subSkillType)) {
-            return RandomChanceUtil.checkRandomChanceExecutionSuccess(new RandomChanceSkill(player, subSkillType, true));
+            return RandomChanceUtil.processProbabilityResults(new SkillProbabilityWrapper(player, subSkillType, true));
         }
 
         return false;

+ 22 - 0
src/main/java/com/gmail/nossr50/util/Misc.java

@@ -344,4 +344,26 @@ public final class Misc {
             experienceOrb.setExperience(orbExpValue);
         }
     }
+
+//    public static void hackyUnitTest(@NotNull McMMOPlayer normalPlayer) {
+//        mcMMO.p.getLogger().info("Starting hacky unit test...");
+//        int iterations = 1000000;
+//        double ratioDivisor = 10000; //10000 because we run the test 1,000,000 times
+//        double expectedFailRate = 100.0D - RandomChanceUtil.getRandomChanceExecutionSuccess(normalPlayer.getPlayer(), SubSkillType.MINING_MOTHER_LODE, true);
+//
+//        double win = 0, loss = 0;
+//        for(int x = 0; x < iterations; x++) {
+//            if(RandomChanceUtil.checkRandomChanceExecutionSuccess(normalPlayer.getPlayer(), SubSkillType.MINING_MOTHER_LODE, true)) {
+//                win++;
+//            } else {
+//                loss++;
+//            }
+//        }
+//
+//        double lossRatio = (loss / ratioDivisor);
+//        mcMMO.p.getLogger().info("Expected Fail Rate: "+expectedFailRate);
+//        mcMMO.p.getLogger().info("Loss Ratio for hacky test: "+lossRatio);
+////        Assert.assertEquals(lossRatio, expectedFailRate, 0.01D);
+//    }
+
 }

+ 14 - 0
src/main/java/com/gmail/nossr50/util/random/Probability.java

@@ -0,0 +1,14 @@
+package com.gmail.nossr50.util.random;
+
+public interface Probability {
+    /**
+     * The value of this Probability
+     * Should return a result between 0 and 1 (inclusive)
+     * 1 should represent something that will always succeed
+     * 0.5 should represent something that succeeds around half the time
+     * etc
+     *
+     * @return the value of probability
+     */
+    double getValue();
+}

+ 77 - 0
src/main/java/com/gmail/nossr50/util/random/ProbabilityFactory.java

@@ -0,0 +1,77 @@
+package com.gmail.nossr50.util.random;
+
+import com.gmail.nossr50.config.AdvancedConfig;
+import com.gmail.nossr50.datatypes.player.McMMOPlayer;
+import com.gmail.nossr50.datatypes.skills.SubSkillType;
+import com.gmail.nossr50.util.player.UserManager;
+import org.bukkit.entity.Player;
+import org.jetbrains.annotations.NotNull;
+import org.jetbrains.annotations.Nullable;
+
+public class ProbabilityFactory {
+
+    public static @NotNull Probability ofPercentageValue(double percentageValue) {
+        return new ProbabilityImpl(probabilityFromPercent(percentageValue));
+    }
+
+    public static @NotNull Probability ofSubSkill(@Nullable Player player,
+                                                  @NotNull SubSkillType subSkillType,
+                                                  @NotNull SkillProbabilityType skillProbabilityType) throws InvalidStaticChance, RuntimeException {
+
+        switch (skillProbabilityType) {
+            case DYNAMIC_CONFIGURABLE:
+                double probabilityCeiling;
+                double xCeiling;
+                double xPos;
+
+                if (player != null) {
+                    McMMOPlayer mmoPlayer = UserManager.getPlayer(player);
+                    if(mmoPlayer != null)
+                        xPos = mmoPlayer.getSkillLevel(subSkillType.getParentSkill());
+                    else
+                        xPos = 0;
+                } else {
+                    xPos = 0;
+                }
+
+                //Probability ceiling is configurable in this type
+                probabilityCeiling = AdvancedConfig.getInstance().getMaximumProbability(subSkillType);
+                //The xCeiling is configurable in this type
+                xCeiling = AdvancedConfig.getInstance().getMaxBonusLevel(subSkillType);
+                return new ProbabilityImpl(xPos, xCeiling, probabilityCeiling);
+            case STATIC_CONFIGURABLE:
+                return ofPercentageValue(getStaticRandomChance(subSkillType));
+            default:
+                throw new RuntimeException("No case in switch statement for Skill Probability Type!");
+        }
+    }
+
+    /**
+     * Convert a probability from a percentage
+     * @param percentage value to convert
+     * @return 0 -> 1 inclusive representation of probability
+     */
+    public static double probabilityFromPercent(double percentage) {
+        return percentage / 100;
+    }
+
+    /**
+     * 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
+     */
+    private 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();
+        }
+    }
+}

+ 63 - 0
src/main/java/com/gmail/nossr50/util/random/ProbabilityImpl.java

@@ -0,0 +1,63 @@
+package com.gmail.nossr50.util.random;
+
+import com.gmail.nossr50.api.exceptions.ValueOutOfBoundsException;
+import com.google.common.base.Objects;
+
+public class ProbabilityImpl implements Probability {
+
+    private final double probabilityValue;
+
+    /**
+     * Create a probability with a static value
+     *
+     * @param staticProbability the value to assign to this probability
+     */
+    public ProbabilityImpl(double staticProbability) throws ValueOutOfBoundsException {
+        if(staticProbability > 1) {
+            throw new ValueOutOfBoundsException("Value should never be above 1 for Probability! This suggests a coding mistake, contact the devs!");
+        } else if (staticProbability < 0) {
+            throw new ValueOutOfBoundsException("Value should never be negative for Probability! This suggests a coding mistake, contact the devs!");
+        }
+
+        probabilityValue = staticProbability;
+    }
+
+    public ProbabilityImpl(double xPos, double xCeiling, double probabilityCeiling) throws ValueOutOfBoundsException {
+        if(probabilityCeiling > 100) {
+            throw new ValueOutOfBoundsException("Probability Ceiling should never be above 100!");
+        } else if (probabilityCeiling < 0) {
+            throw new ValueOutOfBoundsException("Probability Ceiling should never be below 0!");
+        }
+
+        //Get the percent success, this will be from 0-100
+        double probabilityPercent = (probabilityCeiling * (xPos / xCeiling));
+
+        //Convert to a 0-1 floating point representation
+        this.probabilityValue = probabilityPercent / 100.0D;
+    }
+
+    @Override
+    public double getValue() {
+        return probabilityValue;
+    }
+
+    @Override
+    public String toString() {
+        return "ProbabilityImpl{" +
+                "probabilityValue=" + probabilityValue +
+                '}';
+    }
+
+    @Override
+    public boolean equals(Object o) {
+        if (this == o) return true;
+        if (o == null || getClass() != o.getClass()) return false;
+        ProbabilityImpl that = (ProbabilityImpl) o;
+        return Double.compare(that.probabilityValue, probabilityValue) == 0;
+    }
+
+    @Override
+    public int hashCode() {
+        return Objects.hashCode(probabilityValue);
+    }
+}

+ 0 - 18
src/main/java/com/gmail/nossr50/util/random/RandomChanceExecution.java

@@ -1,18 +0,0 @@
-package com.gmail.nossr50.util.random;
-
-public interface RandomChanceExecution {
-    /**
-     * Gets the XPos used in the formula for success
-     *
-     * @return value of x for our success probability graph
-     */
-    double getXPos();
-
-    /**
-     * The maximum odds for this RandomChanceExecution
-     * For example, if this value is 10, then 10% odds would be the maximum and would be achieved only when xPos equaled the LinearCurvePeak
-     *
-     * @return maximum probability odds from 0.00 (no chance of ever happened) to 100.0 (probability can be guaranteed)
-     */
-    double getProbabilityCap();
-}

+ 0 - 176
src/main/java/com/gmail/nossr50/util/random/RandomChanceSkill.java

@@ -1,176 +0,0 @@
-package com.gmail.nossr50.util.random;
-
-import com.gmail.nossr50.datatypes.player.McMMOPlayer;
-import com.gmail.nossr50.datatypes.skills.SubSkillType;
-import com.gmail.nossr50.util.Permissions;
-import com.gmail.nossr50.util.player.UserManager;
-import org.bukkit.entity.Player;
-import org.jetbrains.annotations.NotNull;
-import org.jetbrains.annotations.Nullable;
-
-public class RandomChanceSkill implements RandomChanceExecution {
-    protected final double probabilityCap;
-    protected final boolean isLucky;
-    protected int skillLevel;
-    protected final double resultModifier;
-    protected final double maximumBonusLevelCap;
-
-    public RandomChanceSkill(@Nullable Player player, @NotNull SubSkillType subSkillType, double resultModifier) {
-        this.probabilityCap = RandomChanceUtil.LINEAR_CURVE_VAR;
-
-        final McMMOPlayer mcMMOPlayer = UserManager.getPlayer(player);
-        if (player != null && mcMMOPlayer != null) {
-            this.skillLevel = mcMMOPlayer.getSkillLevel(subSkillType.getParentSkill());
-        } else {
-            this.skillLevel = 0;
-        }
-
-        if (player != null)
-            isLucky = Permissions.lucky(player, subSkillType.getParentSkill());
-        else
-            isLucky = false;
-
-        this.resultModifier = resultModifier;
-        this.maximumBonusLevelCap = RandomChanceUtil.getMaxBonusLevelCap(subSkillType);
-    }
-
-    public RandomChanceSkill(@Nullable Player player, @NotNull SubSkillType subSkillType) {
-        this.probabilityCap = RandomChanceUtil.LINEAR_CURVE_VAR;
-
-        final McMMOPlayer mcMMOPlayer = UserManager.getPlayer(player);
-        if (player != null && mcMMOPlayer != null) {
-            this.skillLevel = mcMMOPlayer.getSkillLevel(subSkillType.getParentSkill());
-        } else {
-            this.skillLevel = 0;
-        }
-
-        if (player != null)
-            isLucky = Permissions.lucky(player, subSkillType.getParentSkill());
-        else
-            isLucky = false;
-
-        this.resultModifier = 1.0D;
-        this.maximumBonusLevelCap = RandomChanceUtil.getMaxBonusLevelCap(subSkillType);
-    }
-
-    public RandomChanceSkill(@Nullable Player player, @NotNull SubSkillType subSkillType, boolean hasCap) {
-        if (hasCap)
-            this.probabilityCap = RandomChanceUtil.getMaximumProbability(subSkillType);
-        else
-            this.probabilityCap = RandomChanceUtil.LINEAR_CURVE_VAR;
-
-        final McMMOPlayer mcMMOPlayer = UserManager.getPlayer(player);
-        if (player != null && mcMMOPlayer != null) {
-            this.skillLevel = mcMMOPlayer.getSkillLevel(subSkillType.getParentSkill());
-        } else {
-            this.skillLevel = 0;
-        }
-
-        if (player != null)
-            isLucky = Permissions.lucky(player, subSkillType.getParentSkill());
-        else
-            isLucky = false;
-
-        this.resultModifier = 1.0D;
-        this.maximumBonusLevelCap = RandomChanceUtil.getMaxBonusLevelCap(subSkillType);
-    }
-
-    public RandomChanceSkill(@Nullable Player player, @NotNull SubSkillType subSkillType, boolean hasCap, boolean luckyOverride) {
-        if (hasCap)
-            this.probabilityCap = RandomChanceUtil.getMaximumProbability(subSkillType);
-        else
-            this.probabilityCap = RandomChanceUtil.LINEAR_CURVE_VAR;
-
-        final McMMOPlayer mcMMOPlayer = UserManager.getPlayer(player);
-        if (player != null && mcMMOPlayer != null) {
-            this.skillLevel = mcMMOPlayer.getSkillLevel(subSkillType.getParentSkill());
-        } else {
-            this.skillLevel = 0;
-        }
-
-        isLucky = luckyOverride;
-
-        this.resultModifier = 1.0D;
-        this.maximumBonusLevelCap = RandomChanceUtil.getMaxBonusLevelCap(subSkillType);
-    }
-
-    public RandomChanceSkill(@Nullable Player player, @NotNull SubSkillType subSkillType, boolean hasCap, double resultModifier) {
-        if (hasCap)
-            this.probabilityCap = RandomChanceUtil.getMaximumProbability(subSkillType);
-        else
-            this.probabilityCap = RandomChanceUtil.LINEAR_CURVE_VAR;
-
-        final McMMOPlayer mcMMOPlayer = UserManager.getPlayer(player);
-        if (player != null && mcMMOPlayer != null) {
-            this.skillLevel = mcMMOPlayer.getSkillLevel(subSkillType.getParentSkill());
-        } else {
-            this.skillLevel = 0;
-        }
-
-        if (player != null)
-            isLucky = Permissions.lucky(player, subSkillType.getParentSkill());
-        else
-            isLucky = false;
-
-        this.resultModifier = resultModifier;
-        this.maximumBonusLevelCap = RandomChanceUtil.getMaxBonusLevelCap(subSkillType);
-    }
-
-    /**
-     * Gets the skill level of the player who owns this RandomChanceSkill
-     *
-     * @return the current skill level relating to this RandomChanceSkill
-     */
-    public int getSkillLevel() {
-        return skillLevel;
-    }
-
-    /**
-     * Modify the skill level used for this skill's RNG calculations
-     *
-     * @param newSkillLevel new skill level
-     */
-    public void setSkillLevel(int newSkillLevel) {
-        skillLevel = newSkillLevel;
-    }
-
-    /**
-     * The maximum bonus level for this skill
-     * This is when the skills level no longer increases the odds of success
-     * For example, a value of 25 will mean the success chance no longer grows after skill level 25
-     *
-     * @return the maximum bonus from skill level for this skill
-     */
-    public double getMaximumBonusLevelCap() {
-        return maximumBonusLevelCap;
-    }
-
-    /**
-     * Gets the XPos used in the formula for success
-     *
-     * @return value of x for our success probability graph
-     */
-    @Override
-    public double getXPos() {
-        return getSkillLevel();
-    }
-
-    /**
-     * The maximum odds for this RandomChanceExecution
-     * For example, if this value is 10, then 10% odds would be the maximum and would be achieved only when xPos equaled the LinearCurvePeak
-     *
-     * @return maximum probability odds from 0.00 (no chance of ever happened) to 100.0 (probability can be guaranteed)
-     */
-    @Override
-    public double getProbabilityCap() {
-        return probabilityCap;
-    }
-
-    public boolean isLucky() {
-        return isLucky;
-    }
-
-    public double getResultModifier() {
-        return resultModifier;
-    }
-}

+ 0 - 61
src/main/java/com/gmail/nossr50/util/random/RandomChanceSkillStatic.java

@@ -1,61 +0,0 @@
-package com.gmail.nossr50.util.random;
-
-import com.gmail.nossr50.datatypes.skills.SubSkillType;
-import org.bukkit.entity.Player;
-import org.jetbrains.annotations.NotNull;
-import org.jetbrains.annotations.Nullable;
-
-public class RandomChanceSkillStatic extends RandomChanceSkill {
-    private final double xPos;
-
-    public RandomChanceSkillStatic(double xPos, @Nullable Player player, @NotNull SubSkillType subSkillType) {
-        super(player, subSkillType);
-
-        this.xPos = xPos;
-    }
-
-    public RandomChanceSkillStatic(double xPos, @Nullable Player player, @NotNull SubSkillType subSkillType, boolean luckyOverride) {
-        super(player, subSkillType, false, luckyOverride);
-
-        this.xPos = xPos;
-    }
-
-    public RandomChanceSkillStatic(double xPos, @Nullable Player player, @NotNull SubSkillType subSkillType, double resultModifier) {
-        super(player, subSkillType, resultModifier);
-
-        this.xPos = xPos;
-    }
-
-    /**
-     * Gets the XPos used in the formula for success
-     *
-     * @return value of x for our success probability graph
-     */
-    @Override
-    public double getXPos() {
-        return xPos;
-    }
-
-    /**
-     * The maximum odds for this RandomChanceExecution
-     * For example, if this value is 10, then 10% odds would be the maximum and would be achieved only when xPos equaled the LinearCurvePeak
-     *
-     * @return maximum probability odds from 0.00 (no chance of ever happened) to 100.0 (probability can be guaranteed)
-     */
-    @Override
-    public double getProbabilityCap() {
-        return probabilityCap;
-    }
-
-    /**
-     * The maximum bonus level for this skill
-     * This is when the skills level no longer increases the odds of success
-     * For example, a value of 25 will mean the success chance no longer grows after skill level 25
-     *
-     * @return the maximum bonus from skill level for this skill
-     */
-    @Override
-    public double getMaximumBonusLevelCap() {
-        return 100;
-    }
-}

+ 0 - 38
src/main/java/com/gmail/nossr50/util/random/RandomChanceStatic.java

@@ -1,38 +0,0 @@
-package com.gmail.nossr50.util.random;
-
-public class RandomChanceStatic implements RandomChanceExecution {
-    private final double xPos;
-    private final double probabilityCap;
-    private final boolean isLucky;
-
-    public RandomChanceStatic(double xPos, boolean isLucky) {
-        this.xPos = xPos;
-        this.probabilityCap = xPos;
-        this.isLucky = isLucky;
-    }
-
-    /**
-     * Gets the XPos used in the formula for success
-     *
-     * @return value of x for our success probability graph
-     */
-    @Override
-    public double getXPos() {
-        return xPos;
-    }
-
-    /**
-     * The maximum odds for this RandomChanceExecution
-     * For example, if this value is 10, then 10% odds would be the maximum and would be achieved only when xPos equaled the LinearCurvePeak
-     *
-     * @return maximum probability odds from 0.00 (no chance of ever happened) to 100.0 (probability can be guaranteed)
-     */
-    @Override
-    public double getProbabilityCap() {
-        return probabilityCap;
-    }
-
-    public boolean isLucky() {
-        return isLucky;
-    }
-}

+ 37 - 284
src/main/java/com/gmail/nossr50/util/random/RandomChanceUtil.java

@@ -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);
-    }
 }

+ 6 - 0
src/main/java/com/gmail/nossr50/util/random/SkillProbabilityType.java

@@ -0,0 +1,6 @@
+package com.gmail.nossr50.util.random;
+
+public enum SkillProbabilityType {
+    DYNAMIC_CONFIGURABLE, //Has multiple values used for calculation (taken from config files)
+    STATIC_CONFIGURABLE, //A single value used for calculations (taken from config files)
+}

+ 90 - 0
src/main/java/com/gmail/nossr50/util/skills/SkillUtils.java

@@ -14,9 +14,11 @@ import com.gmail.nossr50.locale.LocaleLoader;
 import com.gmail.nossr50.mcMMO;
 import com.gmail.nossr50.util.ItemUtils;
 import com.gmail.nossr50.util.Misc;
+import com.gmail.nossr50.util.Permissions;
 import com.gmail.nossr50.util.compat.layers.persistentdata.AbstractPersistentDataLayer;
 import com.gmail.nossr50.util.player.NotificationManager;
 import com.gmail.nossr50.util.player.UserManager;
+import com.gmail.nossr50.util.random.*;
 import com.gmail.nossr50.util.text.StringUtils;
 import org.bukkit.Bukkit;
 import org.bukkit.Location;
@@ -334,4 +336,92 @@ public final class SkillUtils {
 
         return quantity;
     }
+
+    /**
+     * This is one of several Skill RNG check methods
+     * This helper method is for specific {@link SubSkillType}, which help mcMMO understand where the RNG values used in our calculations come from from this {@link SubSkillType}
+     *
+     * 1) Determine where the RNG values come from for the passed {@link SubSkillType}
+     *  NOTE: In the config file, there are values which are static and which are more dynamic, this is currently a bit hardcoded and will need to be updated manually
+     *
+     * 2) Determine whether or not to use Lucky multiplier and influence the outcome
+     *
+     * 3) Creates a {@link Probability} and pipes it to {@link RandomChanceUtil} which processes the result and returns it
+     *
+     * @param subSkillType target subskill
+     * @param player target player, can be null (null players are given odds equivalent to a player with no levels or luck)
+     * @return true if the Skill RNG succeeds, false if it fails
+     */
+    public static boolean isSkillRNGSuccessful(@NotNull SubSkillType subSkillType, @Nullable Player player) {
+        try {
+            //Process probability
+            Probability probability = getSubSkillProbability(subSkillType, player);
+            //Player can be null
+            boolean isLucky = player != null && Permissions.lucky(player, subSkillType.getParentSkill());
+
+            if(isLucky) {
+                return RandomChanceUtil.processProbability(probability, RandomChanceUtil.LUCKY_MODIFIER);
+            } else {
+                return RandomChanceUtil.processProbability(probability);
+            }
+
+        } catch (RuntimeException | InvalidStaticChance e) {
+            e.printStackTrace();
+        }
+
+        return false;
+    }
+
+    /**
+     * This is one of several Skill RNG check methods
+     * This helper method is specific to static value RNG, which can be influenced by a player's Luck
+     *
+     * @param primarySkillType the related primary skill
+     * @param player the target player, can be null (null players have the worst odds)
+     * @param probabilityPercentage the probability of this player succeeding in "percentage" format (0-100 inclusive)
+     * @return true if the RNG succeeds, false if it fails
+     */
+    public static boolean isStaticSkillRNGSuccessful(@NotNull PrimarySkillType primarySkillType, @Nullable Player player, double probabilityPercentage) {
+        //Grab a probability converted from a "percentage" value
+        Probability probability = ProbabilityFactory.ofPercentageValue(probabilityPercentage);
+
+        return isStaticSkillRNGSuccessful(primarySkillType, player, probability);
+    }
+
+    /**
+     * This is one of several Skill RNG check methods
+     * This helper method is specific to static value RNG, which can be influenced by a player's Luck
+     *
+     * @param primarySkillType the related primary skill
+     * @param player the target player, can be null (null players have the worst odds)
+     * @param probability the probability of this player succeeding
+     * @return true if the RNG succeeds, false if it fails
+     */
+    public static boolean isStaticSkillRNGSuccessful(@NotNull PrimarySkillType primarySkillType, @Nullable Player player, @NotNull Probability probability) {
+        boolean isLucky = player != null && Permissions.lucky(player, primarySkillType);
+
+        if(isLucky) {
+            return RandomChanceUtil.processProbability(probability, RandomChanceUtil.LUCKY_MODIFIER);
+        } else {
+            return RandomChanceUtil.processProbability(probability);
+        }
+    }
+
+    /**
+     * Grab the {@link Probability} for a specific {@link SubSkillType} for a specific {@link Player}
+     *
+     * @param subSkillType target subskill
+     * @param player target player
+     * @return the Probability of this skill succeeding
+     * @throws InvalidStaticChance when a skill that does not have a hard coded static chance and it is asked for
+     * @throws RuntimeException
+     */
+    public static @NotNull Probability getSubSkillProbability(@NotNull SubSkillType subSkillType, @Nullable Player player) throws InvalidStaticChance, RuntimeException {
+        SkillProbabilityType skillProbabilityType = SkillProbabilityType.DYNAMIC_CONFIGURABLE;
+
+        if(subSkillType == SubSkillType.TAMING_FAST_FOOD_SERVICE || subSkillType == SubSkillType.AXES_ARMOR_IMPACT || subSkillType == SubSkillType.AXES_GREATER_IMPACT)
+            skillProbabilityType = SkillProbabilityType.STATIC_CONFIGURABLE;
+
+        return ProbabilityFactory.ofSubSkill(player, subSkillType, skillProbabilityType);
+    }
 }

+ 22 - 22
src/test/java/com/gmail/nossr50/util/random/RandomChanceTest.java

@@ -21,7 +21,7 @@
 ////TODO: Rewrite the entire com.gmail.nossr50.util.random package, it was written in haste and it disgusts me
 ////TODO: Add more tests for the other types of random dice rolls
 //@RunWith(PowerMockRunner.class)
-//@PrepareForTest({RandomChanceUtil.class, UserManager.class})
+//@PrepareForTest({RandomChanceUtil.class, UserManager.class, PrimarySkillType.class})
 //public class RandomChanceTest {
 //
 //    private Player luckyPlayer;
@@ -37,12 +37,12 @@
 //
 //    @Before
 //    public void setUpMock() {
-//        primarySkillType = PrimarySkillType.HERBALISM;
-//        subSkillType = SubSkillType.HERBALISM_GREEN_THUMB;
+//        primarySkillType = PrimarySkillType.MINING;
+//        subSkillType = SubSkillType.MINING_MOTHER_LODE;
 //
 //        //TODO: Likely needs to be changed per skill if more tests were added
-//        PowerMockito.stub(PowerMockito.method(RandomChanceUtil.class, "getMaximumProbability", subSkillType.getClass())).toReturn(100D);
-//        PowerMockito.stub(PowerMockito.method(RandomChanceUtil.class, "getMaxBonusLevelCap", subSkillType.getClass())).toReturn(1000D);
+//        PowerMockito.stub(PowerMockito.method(RandomChanceUtil.class, "getMaximumProbability", subSkillType.getClass())).toReturn(10.0D);
+//        PowerMockito.stub(PowerMockito.method(RandomChanceUtil.class, "getMaxBonusLevelCap", subSkillType.getClass())).toReturn(10000D);
 //
 //        normalPlayer = mock(Player.class);
 //        luckyPlayer = mock(Player.class);
@@ -62,39 +62,39 @@
 //        Mockito.when(Permissions.lucky(luckyPlayer, primarySkillType)).thenReturn(true);
 //        Mockito.when(Permissions.lucky(normalPlayer, primarySkillType)).thenReturn(false);
 //
-//        Mockito.when(mmoPlayerNormal.getSkillLevel(primarySkillType)).thenReturn(800);
-//        Mockito.when(mmoPlayerLucky.getSkillLevel(primarySkillType)).thenReturn(800);
+//        Mockito.when(mmoPlayerNormal.getSkillLevel(primarySkillType)).thenReturn(2150);
+//        Mockito.when(mmoPlayerLucky.getSkillLevel(primarySkillType)).thenReturn(2150);
 //    }
 //
 //    @Test
 //    public void testLuckyChance() {
 //        System.out.println(testASCIIHeader);
 //        System.out.println("Testing success odds to fall within expected values...");
-//        assertEquals(80D, getSuccessChance(mmoPlayerNormal),0D);
-//        assertEquals(80D * RandomChanceUtil.LUCKY_MODIFIER, getSuccessChance(mmoPlayerLucky),0D);
+//        assertEquals(2.15D, getSuccessChance(mmoPlayerNormal),0.00D);
+//        assertEquals(2.15D * RandomChanceUtil.LUCKY_MODIFIER, getSuccessChance(mmoPlayerLucky),0.00D);
 //    }
 //
-//    @Test
-//    public void testNeverFailsSuccessLuckyPlayer() {
-//        System.out.println(testASCIIHeader);
-//        System.out.println("Test - Lucky Player with 80% base success should never fail (10,000 iterations)");
-//        for(int x = 0; x < 10000; x++) {
-//            Assert.assertTrue(RandomChanceUtil.checkRandomChanceExecutionSuccess(luckyPlayer, SubSkillType.HERBALISM_GREEN_THUMB, true));
-//            if(x == 10000-1)
-//                System.out.println("They never failed!");
-//        }
-//    }
+////    @Test
+////    public void testNeverFailsSuccessLuckyPlayer() {
+////        System.out.println(testASCIIHeader);
+////        System.out.println("Test - Lucky Player with 80% base success should never fail (10,000 iterations)");
+////        for(int x = 0; x < 10000; x++) {
+////            Assert.assertTrue(RandomChanceUtil.checkRandomChanceExecutionSuccess(luckyPlayer, SubSkillType.HERBALISM_GREEN_THUMB, true));
+////            if(x == 10000-1)
+////                System.out.println("They never failed!");
+////        }
+////    }
 //
 //    @Test
 //    public void testFailsAboutExpected() {
 //        System.out.println(testASCIIHeader);
 //        System.out.println("Test - Player with 800 skill should fail about 20% of the time (100,000 iterations)");
 //        double ratioDivisor = 1000; //1000 because we run the test 100,000 times
-//        double expectedFailRate = 20D;
+//        double expectedFailRate = 100.0D - 2.15D;
 //
 //        double win = 0, loss = 0;
 //        for(int x = 0; x < 100000; x++) {
-//            if(RandomChanceUtil.checkRandomChanceExecutionSuccess(normalPlayer, SubSkillType.HERBALISM_GREEN_THUMB, true)) {
+//            if(RandomChanceUtil.checkRandomChanceExecutionSuccess(normalPlayer, SubSkillType.MINING_MOTHER_LODE, true)) {
 //                win++;
 //            } else {
 //                loss++;
@@ -102,7 +102,7 @@
 //        }
 //
 //        double lossRatio = (loss / ratioDivisor);
-//        Assert.assertEquals(lossRatio, expectedFailRate, 1D);
+//        Assert.assertEquals(lossRatio, expectedFailRate, 0.01D);
 //    }
 //
 //    private double getSuccessChance(@NotNull McMMOPlayer mmoPlayer) {