Merge branch 'master' of https://github.com/mcMMO-Dev/mcMMO into tridentsxbows

This commit is contained in:
nossr50 2021-01-06 10:17:27 -08:00
commit fc10243d6f
21 changed files with 1744 additions and 171 deletions

View File

@ -99,6 +99,15 @@ Version 2.2.000
Parties got unnecessarily complex in my absence, I have removed many party features in order to simplify parties and bring them closer to my vision. I have also added new features which should improve parties where it matters.
About the removed party features, all the features I removed I consider poor quality features and I don't think they belong in mcMMO. Feel free to yell at me in discord if you disagree.
I don't know what genius decided to make parties public by default, when I found out that parties had been changed to such a system I could barely contain my disgust. Parties are back to being private, you get invited by a party leader or party officer. That is the only way to join a party.
Version 2.1.170
Reverted a change that broke compatibility with the mcMMO papi ecloud thingy
Version 2.1.169
Fixed a few memory leaks involving arrows
Fixed mcMMO inappropriately assigning metadata to projectiles not fired from players
Fix mctop not working if locale was set to something other than en_US
mcMMO will now always emulate lure in order to stack it correctly and avoid vanilla bugs
Version 2.1.168
Fixed an IndexOutOfBoundsException error when trying to access UserBlockTracker from an invalid range (thanks t00thpick1)
(API) UserBlockTracker is now the interface by which our block-tracker will be known (thanks t00thpick1)

View File

@ -45,6 +45,6 @@ public class AddlevelsCommand extends ExperienceCommand {
if(isSilent)
return;
player.sendMessage(LocaleLoader.getString("Commands.addlevels.AwardSkill.1", value, rootSkill.getLocalizedName()));
player.sendMessage(LocaleLoader.getString("Commands.addlevels.AwardSkill.1", value, rootSkill.getName()));
}
}

View File

@ -46,6 +46,6 @@ public class AddxpCommand extends ExperienceCommand {
if(isSilent)
return;
player.sendMessage(LocaleLoader.getString("Commands.addxp.AwardSkill", value, rootSkill.getLocalizedName()));
player.sendMessage(LocaleLoader.getString("Commands.addxp.AwardSkill", value, rootSkill.getName()));
}
}

View File

@ -161,7 +161,7 @@ public abstract class ExperienceCommand implements TabExecutor {
sender.sendMessage(LocaleLoader.getString("Commands.addlevels.AwardAll.2", playerName));
}
else {
sender.sendMessage(LocaleLoader.getString("Commands.addlevels.AwardSkill.2", rootSkill.getLocalizedName(), playerName));
sender.sendMessage(LocaleLoader.getString("Commands.addlevels.AwardSkill.2", rootSkill.getName(), playerName));
}
}

View File

@ -51,6 +51,6 @@ public class MmoeditCommand extends ExperienceCommand {
if(isSilent)
return;
player.sendMessage(LocaleLoader.getString("Commands.mmoedit.Modified.1", rootSkill.getLocalizedName(), value));
player.sendMessage(LocaleLoader.getString("Commands.mmoedit.Modified.1", rootSkill.getName(), value));
}
}

View File

@ -142,7 +142,7 @@ public class SkillresetCommand implements TabExecutor {
}
protected void handlePlayerMessageSkill(Player player, RootSkill rootSkill) {
player.sendMessage(LocaleLoader.getString("Commands.Reset.Single", rootSkill.getLocalizedName()));
player.sendMessage(LocaleLoader.getString("Commands.Reset.Single", rootSkill.getName()));
}
private boolean validateArguments(CommandSender sender, String skillName) {
@ -154,7 +154,7 @@ public class SkillresetCommand implements TabExecutor {
sender.sendMessage(LocaleLoader.getString("Commands.addlevels.AwardAll.2", playerName));
}
else {
sender.sendMessage(LocaleLoader.getString("Commands.addlevels.AwardSkill.2", rootSkill.getLocalizedName(), playerName));
sender.sendMessage(LocaleLoader.getString("Commands.addlevels.AwardSkill.2", rootSkill.getName(), playerName));
}
}

View File

@ -59,6 +59,6 @@ public class HardcoreCommand extends HardcoreModeCommand {
rootSkill.setHardcoreStatLossEnabled(enable);
}
mcMMO.p.getServer().broadcastMessage(LocaleLoader.getString("Hardcore.Mode." + (enable ? "Enabled" : "Disabled"), LocaleLoader.getString("Hardcore.DeathStatLoss.Name"), (rootSkill == null ? "all skills" : rootSkill.getLocalizedName())));
mcMMO.p.getServer().broadcastMessage(LocaleLoader.getString("Hardcore.Mode." + (enable ? "Enabled" : "Disabled"), LocaleLoader.getString("Hardcore.DeathStatLoss.Name"), (rootSkill == null ? "all skills" : rootSkill.getName())));
}
}

View File

@ -49,7 +49,7 @@ public abstract class SkillCommand implements TabExecutor {
public SkillCommand(@NotNull RootSkill rootSkill) {
this.rootSkill = CoreSkills.getSkill(primarySkillType);
this.primarySkillType = primarySkillType;
skillName = rootSkill.getLocalizedName();
skillName = rootSkill.getName();
skillGuideCommand = new SkillGuideCommand(rootSkill);
}
@ -177,10 +177,10 @@ public abstract class SkillCommand implements TabExecutor {
{
if(i+1 < parentList.size())
{
parentMessage.append(LocaleLoader.getString("Effects.Child.ParentList", parentList.get(i).getLocalizedName(), mcMMOPlayer.getSkillLevel(parentList.get(i))));
parentMessage.append(LocaleLoader.getString("Effects.Child.ParentList", parentList.get(i).getName(), mcMMOPlayer.getSkillLevel(parentList.get(i))));
parentMessage.append(ChatColor.GRAY).append(", ");
} else {
parentMessage.append(LocaleLoader.getString("Effects.Child.ParentList", parentList.get(i).getLocalizedName(), mcMMOPlayer.getSkillLevel(parentList.get(i))));
parentMessage.append(LocaleLoader.getString("Effects.Child.ParentList", parentList.get(i).getName(), mcMMOPlayer.getSkillLevel(parentList.get(i))));
}
}

View File

@ -22,7 +22,7 @@ public class SkillGuideCommand implements CommandExecutor {
public SkillGuideCommand(@NotNull RootSkill rootSkill) {
this.rootSkill = rootSkill;
header = LocaleLoader.getString("Guides.Header", rootSkill.getLocalizedName());
header = LocaleLoader.getString("Guides.Header", rootSkill.getName());
guide = getGuide(rootSkill);
}

File diff suppressed because it is too large Load Diff

View File

@ -99,10 +99,10 @@ public enum PrimarySkillType {
nonChildSkills.add(skill);
}
for(SubSkillType subSkillType : skill.subSkillTypes)
{
for(SubSkillType subSkillType : skill.subSkillTypes) {
subSkillNames.add(subSkillType.getNiceNameNoSpaces(subSkillType));
}
names.add(skill.getName());
}
@ -234,13 +234,13 @@ public enum PrimarySkillType {
return null;
}
public String getLocalizedName() {
public String getName() {
return StringUtils.getCapitalized(LocaleLoader.getString(StringUtils.getCapitalized(this.toString()) + ".SkillName"));
}
public String getName() {
return StringUtils.getCapitalized(StringUtils.getCapitalized(this.toString()));
}
// public String getName() {
// return StringUtils.getCapitalized(StringUtils.getCapitalized(this.toString()));
// }
public boolean getPermissions(Player player) {
return Permissions.skillEnabled(player, this);

View File

@ -124,7 +124,6 @@ public class EntityListener implements Listener {
if(!WorldGuardManager.getInstance().hasMainFlag(player))
return;
}
}
Entity projectile = event.getProjectile();
@ -142,6 +141,9 @@ public class EntityListener implements Listener {
projectile.setMetadata(mcMMO.bowForceKey, new FixedMetadataValue(pluginRef, Math.min(event.getForce() * AdvancedConfig.getInstance().getForceMultiplier(), 1.0)));
projectile.setMetadata(mcMMO.arrowDistanceKey, new FixedMetadataValue(pluginRef, projectile.getLocation()));
//Cleanup metadata in 1 minute in case normal collection falls through
CombatUtils.cleanupArrowMetadata((Projectile) projectile);
}
}
@EventHandler(ignoreCancelled = true, priority = EventPriority.LOWEST)
@ -169,6 +171,8 @@ public class EntityListener implements Listener {
EntityType entityType = projectile.getType();
if (entityType == EntityType.ARROW || entityType == EntityType.SPECTRAL_ARROW) {
CombatUtils.delayArrowMetaCleanup(projectile); //Cleans up metadata 1 minute from now in case other collection methods fall through
if (!projectile.hasMetadata(mcMMO.bowForceKey))
projectile.setMetadata(mcMMO.bowForceKey, new FixedMetadataValue(pluginRef, 1.0));
@ -244,7 +248,6 @@ public class EntityListener implements Listener {
Entity entity = event.getEntity();
Material notYetReplacedType = block.getState().getType(); //because its from getState() this is the block that hasn't been changed yet, which is likely air/lava/water etc
// When the event is fired for the falling block that changes back to a
// normal block
// event.getBlock().getType() returns AIR
@ -463,13 +466,14 @@ public class EntityListener implements Listener {
LivingEntity livingEntity = (LivingEntity) entityDamageEvent.getEntity();
if(entityDamageEvent.getFinalDamage() >= livingEntity.getHealth()) {
/*
* This sets entity names back to whatever they are supposed to be
*/
//This sets entity names back to whatever they are supposed to be
CombatUtils.fixNames(livingEntity);
}
}
if(entityDamageEvent.getDamager() instanceof Projectile) {
CombatUtils.cleanupArrowMetadata((Projectile) entityDamageEvent.getDamager());
}
}
public boolean checkParties(Cancellable event, Player defendingPlayer, Player attackingPlayer) {

View File

@ -839,7 +839,7 @@ public class PlayerListener implements Listener {
// Do these ACTUALLY have to be lower case to work properly?
for (PrimarySkillType skill : PrimarySkillType.values()) {
String skillName = skill.toString().toLowerCase(Locale.ENGLISH);
String localizedName = skill.getLocalizedName().toLowerCase(Locale.ENGLISH);
String localizedName = skill.getName().toLowerCase(Locale.ENGLISH);
if (lowerCaseCommand.equals(localizedName)) {
event.setMessage(message.replace(command, skillName));

View File

@ -53,7 +53,7 @@ public class McrankCommandDisplayTask extends BukkitRunnable {
// }
rank = skills.get(skill);
sender.sendMessage(LocaleLoader.getString("Commands.mcrank.Skill", skill.getLocalizedName(), (rank == null ? LocaleLoader.getString("Commands.mcrank.Unranked") : rank)));
sender.sendMessage(LocaleLoader.getString("Commands.mcrank.Skill", skill.getName(), (rank == null ? LocaleLoader.getString("Commands.mcrank.Unranked") : rank)));
}
rank = skills.get(null);

View File

@ -63,10 +63,10 @@ public class MctopCommandDisplayTask extends BukkitRunnable {
}
else {
if(sender instanceof Player) {
sender.sendMessage(LocaleLoader.getString("Commands.Skill.Leaderboard", skill.getLocalizedName()));
sender.sendMessage(LocaleLoader.getString("Commands.Skill.Leaderboard", skill.getName()));
}
else {
sender.sendMessage(ChatColor.stripColor(LocaleLoader.getString("Commands.Skill.Leaderboard", skill.getLocalizedName())));
sender.sendMessage(ChatColor.stripColor(LocaleLoader.getString("Commands.Skill.Leaderboard", skill.getName())));
}
}

View File

@ -56,8 +56,7 @@ public class ArcheryManager extends SkillManager {
public double distanceXpBonusMultiplier(@NotNull LivingEntity target, @NotNull Entity arrow) {
//Hacky Fix - some plugins spawn arrows and assign them to players after the ProjectileLaunchEvent fires
if(!arrow.hasMetadata(mcMMO.arrowDistanceKey))
return arrow.getLocation().distance(target.getLocation());
return 1;
Location firedLocation = (Location) arrow.getMetadata(mcMMO.arrowDistanceKey).get(0).value();
Location targetLocation = target.getLocation();

View File

@ -265,7 +265,7 @@ public class FishingManager extends SkillManager {
int convertedLureBonus = 0;
//This avoids a Minecraft bug where lure levels above 3 break fishing
if(lureLevel > 3) {
if(lureLevel > 0) {
masterAnglerCompatibilityLayer.setApplyLure(fishHook, false);
convertedLureBonus = lureLevel * 100;
}

View File

@ -37,7 +37,7 @@ public final class CommandRegistrationManager {
private static void registerSkillCommands() {
for (PrimarySkillType skill : PrimarySkillType.values()) {
String commandName = skill.toString().toLowerCase(Locale.ENGLISH);
String localizedName = skill.getLocalizedName().toLowerCase(Locale.ENGLISH);
String localizedName = skill.getName().toLowerCase(Locale.ENGLISH);
PluginCommand command;

View File

@ -94,7 +94,7 @@ public class ScoreboardManager {
int i = 0;
for (PrimarySkillType type : PrimarySkillType.values()) {
// Include child skills
skillLabelBuilder.put(type, getShortenedName(colors.get(i) + type.getLocalizedName(), false));
skillLabelBuilder.put(type, getShortenedName(colors.get(i) + type.getName(), false));
if (type.getSuperAbilityType() != null) {
abilityLabelBuilder.put(type.getSuperAbilityType(), getShortenedName(colors.get(i) + type.getSuperAbilityType().getLocalizedName()));
@ -116,7 +116,7 @@ public class ScoreboardManager {
else {
for (PrimarySkillType type : PrimarySkillType.values()) {
// Include child skills
skillLabelBuilder.put(type, getShortenedName(ChatColor.GREEN + type.getLocalizedName()));
skillLabelBuilder.put(type, getShortenedName(ChatColor.GREEN + type.getName()));
if (type.getSuperAbilityType() != null) {
abilityLabelBuilder.put(type.getSuperAbilityType(), formatAbility(type.getSuperAbilityType().getLocalizedName()));

View File

@ -24,16 +24,19 @@ 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.google.common.collect.ImmutableMap;
import org.bukkit.Bukkit;
import org.bukkit.GameMode;
import org.bukkit.Material;
import org.bukkit.attribute.Attribute;
import org.bukkit.attribute.AttributeInstance;
import org.bukkit.enchantments.Enchantment;
import org.bukkit.entity.*;
import org.bukkit.event.entity.EntityDamageByEntityEvent;
import org.bukkit.event.entity.EntityDamageEvent;
import org.bukkit.event.entity.EntityDamageEvent.DamageCause;
import org.bukkit.event.entity.EntityDamageEvent.DamageModifier;
import org.bukkit.inventory.ItemStack;
import org.bukkit.metadata.FixedMetadataValue;
import org.bukkit.metadata.MetadataValue;
import org.bukkit.potion.PotionEffectType;
import org.bukkit.projectiles.ProjectileSource;
@ -288,6 +291,7 @@ public final class CombatUtils {
//Make sure the profiles been loaded
if(mmoPlayer == null) {
cleanupArrowMetadata(arrow);
return;
}
@ -326,8 +330,10 @@ public final class CombatUtils {
"Force Multiplier: "+forceMultiplier,
"Initial Damage: "+initialDamage,
"Final Damage: "+finalDamage);
processCombatXP(mmoPlayer, target, PrimarySkillType.ARCHERY, distanceMultiplier);
//Clean data
cleanupArrowMetadata(arrow);
}
private static void processCrossbowCombat(LivingEntity target, Player player, EntityDamageByEntityEvent event, Projectile arrow) {
@ -512,6 +518,9 @@ public final class CombatUtils {
}
}
}
} else {
//Cleanup Arrow
cleanupArrowMetadata(arrow);
}
if (target.getType() != EntityType.CREEPER && !Misc.isNPCEntityExcludingVillagers(player) && PrimarySkillType.TAMING.getPermissions(player)) {
@ -1122,4 +1131,32 @@ public final class CombatUtils {
attributeInstance.setBaseValue(normalSpeed * multiplier);
}
}
/**
* Clean up metadata from a projectile
*
* @param entity projectile
*/
public static void cleanupArrowMetadata(@NotNull Projectile entity) {
if(entity.hasMetadata(mcMMO.infiniteArrowKey)) {
entity.removeMetadata(mcMMO.infiniteArrowKey, mcMMO.p);
}
if(entity.hasMetadata(mcMMO.bowForceKey)) {
entity.removeMetadata(mcMMO.bowForceKey, mcMMO.p);
}
if(entity.hasMetadata(mcMMO.arrowDistanceKey)) {
entity.removeMetadata(mcMMO.arrowDistanceKey, mcMMO.p);
}
}
/**
* Clean up metadata from a projectile after a minute has passed
*
* @param entity the projectile
*/
public static void delayArrowMetaCleanup(@NotNull Projectile entity) {
Bukkit.getServer().getScheduler().runTaskLater(mcMMO.p, () -> { cleanupArrowMetadata(entity);}, 20*60);
}
}

View File

@ -1,116 +1,116 @@
package com.gmail.nossr50.util.random;
import com.gmail.nossr50.datatypes.player.McMMOPlayer;
import com.gmail.nossr50.datatypes.skills.PrimarySkillType;
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.junit.Assert;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mockito;
import org.powermock.api.mockito.PowerMockito;
import org.powermock.core.classloader.annotations.PrepareForTest;
import org.powermock.modules.junit4.PowerMockRunner;
import static org.mockito.Mockito.mock;
//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})
public class RandomChanceTest {
private Player luckyPlayer;
private McMMOPlayer mmoPlayerLucky;
private Player normalPlayer;
private McMMOPlayer mmoPlayerNormal;
private SubSkillType subSkillType;
private PrimarySkillType primarySkillType;
private final String testASCIIHeader = "---- mcMMO Tests ----";
@Before
public void setUpMock() {
primarySkillType = PrimarySkillType.HERBALISM;
subSkillType = SubSkillType.HERBALISM_GREEN_THUMB;
//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);
normalPlayer = mock(Player.class);
luckyPlayer = mock(Player.class);
mmoPlayerNormal = mock(McMMOPlayer.class);
mmoPlayerLucky = mock(McMMOPlayer.class);
PowerMockito.mockStatic(UserManager.class);
Mockito.when(UserManager.getPlayer(normalPlayer)).thenReturn(mmoPlayerNormal);
Mockito.when(UserManager.getPlayer(luckyPlayer)).thenReturn(mmoPlayerLucky);
Mockito.when(mmoPlayerNormal.getPlayer()).thenReturn(normalPlayer);
Mockito.when(mmoPlayerLucky.getPlayer()).thenReturn(luckyPlayer);
//Lucky player has the lucky permission
//Normal player doesn't have any lucky permission
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);
}
@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);
}
@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 win = 0, loss = 0;
for(int x = 0; x < 100000; x++) {
if(RandomChanceUtil.checkRandomChanceExecutionSuccess(normalPlayer, SubSkillType.HERBALISM_GREEN_THUMB, true)) {
win++;
} else {
loss++;
}
}
double lossRatio = (loss / ratioDivisor);
Assert.assertEquals(lossRatio, expectedFailRate, 1D);
}
private double getSuccessChance(@NotNull McMMOPlayer mmoPlayer) {
RandomChanceSkill randomChanceSkill = new RandomChanceSkill(mmoPlayer.getPlayer(), subSkillType, true);
return RandomChanceUtil.calculateChanceOfSuccess(randomChanceSkill);
}
private void assertEquals(double expected, double actual, double delta) {
Assert.assertEquals(expected, actual, delta);
}
}
//package com.gmail.nossr50.util.random;
//
//import com.gmail.nossr50.datatypes.player.McMMOPlayer;
//import com.gmail.nossr50.datatypes.skills.PrimarySkillType;
//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.junit.Assert;
//import org.junit.Before;
//import org.junit.Test;
//import org.junit.runner.RunWith;
//import org.mockito.Mockito;
//import org.powermock.api.mockito.PowerMockito;
//import org.powermock.core.classloader.annotations.PrepareForTest;
//import org.powermock.modules.junit4.PowerMockRunner;
//
//import static org.mockito.Mockito.mock;
//
////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})
//public class RandomChanceTest {
//
// private Player luckyPlayer;
// private McMMOPlayer mmoPlayerLucky;
//
// private Player normalPlayer;
// private McMMOPlayer mmoPlayerNormal;
//
// private SubSkillType subSkillType;
// private PrimarySkillType primarySkillType;
//
// private final String testASCIIHeader = "---- mcMMO Tests ----";
//
// @Before
// public void setUpMock() {
// primarySkillType = PrimarySkillType.HERBALISM;
// subSkillType = SubSkillType.HERBALISM_GREEN_THUMB;
//
// //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);
//
// normalPlayer = mock(Player.class);
// luckyPlayer = mock(Player.class);
//
// mmoPlayerNormal = mock(McMMOPlayer.class);
// mmoPlayerLucky = mock(McMMOPlayer.class);
//
// PowerMockito.mockStatic(UserManager.class);
// Mockito.when(UserManager.getPlayer(normalPlayer)).thenReturn(mmoPlayerNormal);
// Mockito.when(UserManager.getPlayer(luckyPlayer)).thenReturn(mmoPlayerLucky);
//
// Mockito.when(mmoPlayerNormal.getPlayer()).thenReturn(normalPlayer);
// Mockito.when(mmoPlayerLucky.getPlayer()).thenReturn(luckyPlayer);
//
// //Lucky player has the lucky permission
// //Normal player doesn't have any lucky permission
// 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);
// }
//
// @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);
// }
//
// @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 win = 0, loss = 0;
// for(int x = 0; x < 100000; x++) {
// if(RandomChanceUtil.checkRandomChanceExecutionSuccess(normalPlayer, SubSkillType.HERBALISM_GREEN_THUMB, true)) {
// win++;
// } else {
// loss++;
// }
// }
//
// double lossRatio = (loss / ratioDivisor);
// Assert.assertEquals(lossRatio, expectedFailRate, 1D);
// }
//
// private double getSuccessChance(@NotNull McMMOPlayer mmoPlayer) {
// RandomChanceSkill randomChanceSkill = new RandomChanceSkill(mmoPlayer.getPlayer(), subSkillType, true);
// return RandomChanceUtil.calculateChanceOfSuccess(randomChanceSkill);
// }
//
// private void assertEquals(double expected, double actual, double delta) {
// Assert.assertEquals(expected, actual, delta);
// }
//}