diff --git a/Changelog.txt b/Changelog.txt index 23ac1bb42..96529da12 100644 --- a/Changelog.txt +++ b/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) diff --git a/src/main/java/com/gmail/nossr50/api/exceptions/ValueOutOfBoundsException.java b/src/main/java/com/gmail/nossr50/api/exceptions/ValueOutOfBoundsException.java new file mode 100644 index 000000000..4fc4a7a6f --- /dev/null +++ b/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); + } +} diff --git a/src/main/java/com/gmail/nossr50/commands/skills/AcrobaticsCommand.java b/src/main/java/com/gmail/nossr50/commands/skills/AcrobaticsCommand.java index 46ba32b28..bcb5368b5 100644 --- a/src/main/java/com/gmail/nossr50/commands/skills/AcrobaticsCommand.java +++ b/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; diff --git a/src/main/java/com/gmail/nossr50/commands/skills/ArcheryCommand.java b/src/main/java/com/gmail/nossr50/commands/skills/ArcheryCommand.java index ec5c63008..31500493c 100644 --- a/src/main/java/com/gmail/nossr50/commands/skills/ArcheryCommand.java +++ b/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]; } diff --git a/src/main/java/com/gmail/nossr50/commands/skills/AxesCommand.java b/src/main/java/com/gmail/nossr50/commands/skills/AxesCommand.java index 2e61db48e..1a0e74e18 100644 --- a/src/main/java/com/gmail/nossr50/commands/skills/AxesCommand.java +++ b/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]; } diff --git a/src/main/java/com/gmail/nossr50/commands/skills/HerbalismCommand.java b/src/main/java/com/gmail/nossr50/commands/skills/HerbalismCommand.java index ecba231d1..8d8d13b6f 100644 --- a/src/main/java/com/gmail/nossr50/commands/skills/HerbalismCommand.java +++ b/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]; } diff --git a/src/main/java/com/gmail/nossr50/commands/skills/MiningCommand.java b/src/main/java/com/gmail/nossr50/commands/skills/MiningCommand.java index 975e569f4..b94ad33fd 100644 --- a/src/main/java/com/gmail/nossr50/commands/skills/MiningCommand.java +++ b/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) : "")); } diff --git a/src/main/java/com/gmail/nossr50/commands/skills/RepairCommand.java b/src/main/java/com/gmail/nossr50/commands/skills/RepairCommand.java index 8cb9b52fe..c6687a5c4 100644 --- a/src/main/java/com/gmail/nossr50/commands/skills/RepairCommand.java +++ b/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]; } diff --git a/src/main/java/com/gmail/nossr50/commands/skills/SkillCommand.java b/src/main/java/com/gmail/nossr50/commands/skills/SkillCommand.java index ee1ecb202..48e4faf37 100644 --- a/src/main/java/com/gmail/nossr50/commands/skills/SkillCommand.java +++ b/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); } diff --git a/src/main/java/com/gmail/nossr50/commands/skills/SmeltingCommand.java b/src/main/java/com/gmail/nossr50/commands/skills/SmeltingCommand.java index 7b039ea7e..3c251b129 100644 --- a/src/main/java/com/gmail/nossr50/commands/skills/SmeltingCommand.java +++ b/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]; } diff --git a/src/main/java/com/gmail/nossr50/commands/skills/SwordsCommand.java b/src/main/java/com/gmail/nossr50/commands/skills/SwordsCommand.java index 7655ce680..967cd1a22 100644 --- a/src/main/java/com/gmail/nossr50/commands/skills/SwordsCommand.java +++ b/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]; } diff --git a/src/main/java/com/gmail/nossr50/commands/skills/TamingCommand.java b/src/main/java/com/gmail/nossr50/commands/skills/TamingCommand.java index e007799db..639de87c1 100644 --- a/src/main/java/com/gmail/nossr50/commands/skills/TamingCommand.java +++ b/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]; } diff --git a/src/main/java/com/gmail/nossr50/commands/skills/UnarmedCommand.java b/src/main/java/com/gmail/nossr50/commands/skills/UnarmedCommand.java index ee52401e6..f93e948cb 100644 --- a/src/main/java/com/gmail/nossr50/commands/skills/UnarmedCommand.java +++ b/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]; } diff --git a/src/main/java/com/gmail/nossr50/commands/skills/WoodcuttingCommand.java b/src/main/java/com/gmail/nossr50/commands/skills/WoodcuttingCommand.java index a6f763f13..4b282629c 100644 --- a/src/main/java/com/gmail/nossr50/commands/skills/WoodcuttingCommand.java +++ b/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]; } diff --git a/src/main/java/com/gmail/nossr50/datatypes/skills/subskills/acrobatics/Roll.java b/src/main/java/com/gmail/nossr50/datatypes/skills/subskills/acrobatics/Roll.java index 4ea8ad156..4e9fc852a 100644 --- a/src/main/java/com/gmail/nossr50/datatypes/skills/subskills/acrobatics/Roll.java +++ b/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); diff --git a/src/main/java/com/gmail/nossr50/datatypes/treasure/Treasure.java b/src/main/java/com/gmail/nossr50/datatypes/treasure/Treasure.java index 04ce7cca7..664760364 100644 --- a/src/main/java/com/gmail/nossr50/datatypes/treasure/Treasure.java +++ b/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); + } } diff --git a/src/main/java/com/gmail/nossr50/listeners/EntityListener.java b/src/main/java/com/gmail/nossr50/listeners/EntityListener.java index ede2081e6..e653ceb5a 100644 --- a/src/main/java/com/gmail/nossr50/listeners/EntityListener.java +++ b/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); } } diff --git a/src/main/java/com/gmail/nossr50/skills/acrobatics/AcrobaticsManager.java b/src/main/java/com/gmail/nossr50/skills/acrobatics/AcrobaticsManager.java index a4903e123..f022d838a 100644 --- a/src/main/java/com/gmail/nossr50/skills/acrobatics/AcrobaticsManager.java +++ b/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()) { diff --git a/src/main/java/com/gmail/nossr50/skills/archery/ArcheryManager.java b/src/main/java/com/gmail/nossr50/skills/archery/ArcheryManager.java index 8ef8a0e1a..ed0393053 100644 --- a/src/main/java/com/gmail/nossr50/skills/archery/ArcheryManager.java +++ b/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; } diff --git a/src/main/java/com/gmail/nossr50/skills/axes/AxesManager.java b/src/main/java/com/gmail/nossr50/skills/axes/AxesManager.java index ead48f6d8..15c94ee07 100644 --- a/src/main/java/com/gmail/nossr50/skills/axes/AxesManager.java +++ b/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; } diff --git a/src/main/java/com/gmail/nossr50/skills/excavation/ExcavationManager.java b/src/main/java/com/gmail/nossr50/skills/excavation/ExcavationManager.java index 14eda7e32..3ed6f5f2a 100644 --- a/src/main/java/com/gmail/nossr50/skills/excavation/ExcavationManager.java +++ b/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)) { diff --git a/src/main/java/com/gmail/nossr50/skills/fishing/FishingManager.java b/src/main/java/com/gmail/nossr50/skills/fishing/FishingManager.java index b64de6a37..fef8816b5 100644 --- a/src/main/java/com/gmail/nossr50/skills/fishing/FishingManager.java +++ b/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 possibleDrops = Fishing.findPossibleDrops(target); if (possibleDrops == null || possibleDrops.isEmpty()) { diff --git a/src/main/java/com/gmail/nossr50/skills/herbalism/HerbalismManager.java b/src/main/java/com/gmail/nossr50/skills/herbalism/HerbalismManager.java index 4a8104dd9..906d538a4 100644 --- a/src/main/java/com/gmail/nossr50/skills/herbalism/HerbalismManager.java +++ b/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; } diff --git a/src/main/java/com/gmail/nossr50/skills/repair/RepairManager.java b/src/main/java/com/gmail/nossr50/skills/repair/RepairManager.java index 8e8e56a3c..527b34ef2 100644 --- a/src/main/java/com/gmail/nossr50/skills/repair/RepairManager.java +++ b/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; } diff --git a/src/main/java/com/gmail/nossr50/skills/salvage/SalvageManager.java b/src/main/java/com/gmail/nossr50/skills/salvage/SalvageManager.java index 2545a15d2..eaa044c98 100644 --- a/src/main/java/com/gmail/nossr50/skills/salvage/SalvageManager.java +++ b/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 { diff --git a/src/main/java/com/gmail/nossr50/skills/smelting/SmeltingManager.java b/src/main/java/com/gmail/nossr50/skills/smelting/SmeltingManager.java index 518718765..5f48ea5d8 100644 --- a/src/main/java/com/gmail/nossr50/skills/smelting/SmeltingManager.java +++ b/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()); } /* diff --git a/src/main/java/com/gmail/nossr50/skills/swords/SwordsManager.java b/src/main/java/com/gmail/nossr50/skills/swords/SwordsManager.java index 1c3fa3766..b7716667e 100644 --- a/src/main/java/com/gmail/nossr50/skills/swords/SwordsManager.java +++ b/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"); diff --git a/src/main/java/com/gmail/nossr50/skills/taming/TamingManager.java b/src/main/java/com/gmail/nossr50/skills/taming/TamingManager.java index 6a902dd43..2f6c1d86c 100644 --- a/src/main/java/com/gmail/nossr50/skills/taming/TamingManager.java +++ b/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); diff --git a/src/main/java/com/gmail/nossr50/skills/unarmed/UnarmedManager.java b/src/main/java/com/gmail/nossr50/skills/unarmed/UnarmedManager.java index 73bf3dce9..b2fe84017 100644 --- a/src/main/java/com/gmail/nossr50/skills/unarmed/UnarmedManager.java +++ b/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"); diff --git a/src/main/java/com/gmail/nossr50/skills/woodcutting/WoodcuttingManager.java b/src/main/java/com/gmail/nossr50/skills/woodcutting/WoodcuttingManager.java index 4aa7cb478..d86c0d698 100644 --- a/src/main/java/com/gmail/nossr50/skills/woodcutting/WoodcuttingManager.java +++ b/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); } diff --git a/src/main/java/com/gmail/nossr50/util/BlockUtils.java b/src/main/java/com/gmail/nossr50/util/BlockUtils.java index 80c5a3ca1..a951057b5 100644 --- a/src/main/java/com/gmail/nossr50/util/BlockUtils.java +++ b/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; diff --git a/src/main/java/com/gmail/nossr50/util/Misc.java b/src/main/java/com/gmail/nossr50/util/Misc.java index 9b74f110c..1bd24c5e3 100644 --- a/src/main/java/com/gmail/nossr50/util/Misc.java +++ b/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); +// } + } diff --git a/src/main/java/com/gmail/nossr50/util/random/Probability.java b/src/main/java/com/gmail/nossr50/util/random/Probability.java new file mode 100644 index 000000000..0e939bc8b --- /dev/null +++ b/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(); +} diff --git a/src/main/java/com/gmail/nossr50/util/random/ProbabilityFactory.java b/src/main/java/com/gmail/nossr50/util/random/ProbabilityFactory.java new file mode 100644 index 000000000..f3a7db5b2 --- /dev/null +++ b/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(); + } + } +} diff --git a/src/main/java/com/gmail/nossr50/util/random/ProbabilityImpl.java b/src/main/java/com/gmail/nossr50/util/random/ProbabilityImpl.java new file mode 100644 index 000000000..6401ca757 --- /dev/null +++ b/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); + } +} diff --git a/src/main/java/com/gmail/nossr50/util/random/RandomChanceExecution.java b/src/main/java/com/gmail/nossr50/util/random/RandomChanceExecution.java deleted file mode 100644 index e5d51d740..000000000 --- a/src/main/java/com/gmail/nossr50/util/random/RandomChanceExecution.java +++ /dev/null @@ -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(); -} diff --git a/src/main/java/com/gmail/nossr50/util/random/RandomChanceSkill.java b/src/main/java/com/gmail/nossr50/util/random/RandomChanceSkill.java deleted file mode 100644 index 92d91ec83..000000000 --- a/src/main/java/com/gmail/nossr50/util/random/RandomChanceSkill.java +++ /dev/null @@ -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; - } -} diff --git a/src/main/java/com/gmail/nossr50/util/random/RandomChanceSkillStatic.java b/src/main/java/com/gmail/nossr50/util/random/RandomChanceSkillStatic.java deleted file mode 100644 index c96b71d6b..000000000 --- a/src/main/java/com/gmail/nossr50/util/random/RandomChanceSkillStatic.java +++ /dev/null @@ -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; - } -} diff --git a/src/main/java/com/gmail/nossr50/util/random/RandomChanceStatic.java b/src/main/java/com/gmail/nossr50/util/random/RandomChanceStatic.java deleted file mode 100644 index 3204a348d..000000000 --- a/src/main/java/com/gmail/nossr50/util/random/RandomChanceStatic.java +++ /dev/null @@ -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; - } -} diff --git a/src/main/java/com/gmail/nossr50/util/random/RandomChanceUtil.java b/src/main/java/com/gmail/nossr50/util/random/RandomChanceUtil.java index 200893fa7..8641860be 100644 --- a/src/main/java/com/gmail/nossr50/util/random/RandomChanceUtil.java +++ b/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; - } + public static boolean processProbability(@NotNull Probability probability) { + return isSuccessfulRoll(probability.getValue()); } /** - * Checks whether or not the random chance succeeds + * Modify and then Simulate an outcome on a probability and return true or false for the result of that outcome * - * @return true if the random chance succeeds + * @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 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, 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, double resultModifier) { - double chanceOfSuccess = calculateChanceOfSuccess(randomChance); - - //Check the odds - return rollDice(chanceOfSuccess, 100, resultModifier); + private static boolean isSuccessfulRoll(double probabilityValue) { + return probabilityValue >= ThreadLocalRandom.current().nextDouble(1.0D); } /** - * Used for stuff like Excavation, Fishing, etc... + * Return a chance of success in "percentage" format, show to the player in UI elements * - * @param randomChance - * @return - */ - 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); - } - - /** - * Gets the Static Chance for something to activate + * @param player target player + * @param subSkillType target subskill + * @param isLucky whether or not to apply luck modifiers * - * @param randomChance - * @return + * @return "percentage" representation of success */ - 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 ) - * - * @return the chance of success from 0-100 (100 = guaranteed) - */ - 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)); + 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; + } + + return percentageValue; } 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)); - } - - - public static boolean checkRandomStaticChanceExecutionSuccess(@Nullable Player player, @NotNull SubSkillType subSkillType) { - try { - return checkRandomChanceExecutionSuccess(new RandomChanceSkillStatic(getStaticRandomChance(subSkillType), player, subSkillType)); - } catch (InvalidStaticChance invalidStaticChance) { - //Catch invalid static skills - invalidStaticChance.printStackTrace(); - } - - 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(); + return 0; } } - 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); - } } diff --git a/src/main/java/com/gmail/nossr50/util/random/SkillProbabilityType.java b/src/main/java/com/gmail/nossr50/util/random/SkillProbabilityType.java new file mode 100644 index 000000000..95814f6a1 --- /dev/null +++ b/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) +} diff --git a/src/main/java/com/gmail/nossr50/util/skills/SkillUtils.java b/src/main/java/com/gmail/nossr50/util/skills/SkillUtils.java index 08b931764..13c404963 100644 --- a/src/main/java/com/gmail/nossr50/util/skills/SkillUtils.java +++ b/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); + } } diff --git a/src/test/java/com/gmail/nossr50/util/random/RandomChanceTest.java b/src/test/java/com/gmail/nossr50/util/random/RandomChanceTest.java index f28e7e842..d417635fc 100644 --- a/src/test/java/com/gmail/nossr50/util/random/RandomChanceTest.java +++ b/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) {