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

This commit is contained in:
nossr50 2021-01-04 14:15:02 -08:00
commit 8a91791d2d
35 changed files with 393 additions and 307 deletions

View File

@ -102,6 +102,7 @@ Version 2.2.000
Version 2.1.168 Version 2.1.168
Fixed an IndexOutOfBoundsException error when trying to access UserBlockTracker from an invalid range (thanks t00thpick1) 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) (API) UserBlockTracker is now the interface by which our block-tracker will be known (thanks t00thpick1)
Optimized memory access for Acrobatics fall anti-exploit mechanics (thanks t00thpick1)
Version 2.1.167 Version 2.1.167
Fixed a serious dupe bug Fixed a serious dupe bug

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -21,8 +21,8 @@ public class SkillGuideCommand implements CommandExecutor {
private final String invalidPage = LocaleLoader.getString("Guides.Page.Invalid"); private final String invalidPage = LocaleLoader.getString("Guides.Page.Invalid");
public SkillGuideCommand(@NotNull RootSkill rootSkill) { public SkillGuideCommand(@NotNull RootSkill rootSkill) {
rootSkill = CoreSkills.getSkill(rootSkill); this.rootSkill = rootSkill;
header = LocaleLoader.getString("Guides.Header", rootSkill.getName()); header = LocaleLoader.getString("Guides.Header", rootSkill.getLocalizedName());
guide = getGuide(rootSkill); guide = getGuide(rootSkill);
} }

View File

@ -8,7 +8,6 @@ import org.bukkit.ChatColor;
import org.bukkit.Material; import org.bukkit.Material;
import org.bukkit.Tag; import org.bukkit.Tag;
import org.bukkit.configuration.ConfigurationSection; import org.bukkit.configuration.ConfigurationSection;
import org.bukkit.entity.EntityType;
import org.bukkit.inventory.ItemStack; import org.bukkit.inventory.ItemStack;
import org.bukkit.inventory.meta.ItemMeta; import org.bukkit.inventory.meta.ItemMeta;
import org.bukkit.inventory.meta.PotionMeta; import org.bukkit.inventory.meta.PotionMeta;

View File

@ -0,0 +1,41 @@
package com.gmail.nossr50.datatypes;
import com.google.common.collect.HashMultiset;
import org.bukkit.Location;
import java.util.LinkedList;
/**
* This class works with the assumption that you only pass in Block Locations. If locations have differing pitch/yaw, the logic breaks
*/
public class BlockLocationHistory {
private final LinkedList<Location> limitedSizeOrderedList = new LinkedList<>();
private final HashMultiset<Location> lookup = HashMultiset.create();
private final int maxSize;
public BlockLocationHistory(int maxSize) {
this.maxSize = maxSize;
}
/**
* Adds a block location to the history. If the history memory would exceed the max size, it will remove the least recently added block location
*
* @param newItem
*/
public void add(Location newItem) {
limitedSizeOrderedList.addFirst(newItem);
lookup.add(newItem);
if (limitedSizeOrderedList.size() > maxSize)
lookup.remove(limitedSizeOrderedList.removeLast());
}
/**
* Returns true if the block location is in the recorded history
*
* @param targetLoc the block location to search for
* @return true if the block location is in the recorded history
*/
public boolean contains(Location targetLoc) {
return lookup.contains(targetLoc);
}
}

View File

@ -1,57 +0,0 @@
package com.gmail.nossr50.datatypes;
import org.bukkit.Location;
public class LimitedSizeList {
public Location[] limitedSizeOrderedList;
private final int size;
public LimitedSizeList(int size)
{
this.size = size;
limitedSizeOrderedList = new Location[size];
}
/**
* Adds objects to our limited size ordered list
* New objects are added to the front
* @param newItem
*/
public void add(Location newItem)
{
Location[] newList = new Location[size];
for(int i = 0; i < size-1; i++)
{
if(i != 0)
newList[i] = limitedSizeOrderedList[i-1];
else
newList[i] = newItem;
}
limitedSizeOrderedList = newList;
}
/**
* Returns true if the object is anywhere in our list
* @param targetLoc the object to check for
* @return true if the object is in our list
*/
public boolean contains(Location targetLoc)
{
for(Location iter : limitedSizeOrderedList)
{
if(iter == null)
continue;
if(iter.getX() == targetLoc.getX()
&& iter.getY() == targetLoc.getY()
&& iter.getZ() == targetLoc.getZ())
return true;
}
return false;
}
}

View File

@ -234,10 +234,14 @@ public enum PrimarySkillType {
return null; return null;
} }
public String getName() { public String getLocalizedName() {
return StringUtils.getCapitalized(LocaleLoader.getString(StringUtils.getCapitalized(this.toString()) + ".SkillName")); return StringUtils.getCapitalized(LocaleLoader.getString(StringUtils.getCapitalized(this.toString()) + ".SkillName"));
} }
public String getName() {
return StringUtils.getCapitalized(StringUtils.getCapitalized(this.toString()));
}
public boolean getPermissions(Player player) { public boolean getPermissions(Player player) {
return Permissions.skillEnabled(player, this); return Permissions.skillEnabled(player, this);
} }

View File

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

View File

@ -3,18 +3,14 @@ package com.gmail.nossr50.listeners;
import com.gmail.nossr50.config.WorldBlacklist; import com.gmail.nossr50.config.WorldBlacklist;
import com.gmail.nossr50.mcMMO; import com.gmail.nossr50.mcMMO;
import org.bukkit.Chunk; import org.bukkit.Chunk;
import org.bukkit.World;
import org.bukkit.block.BlockState; import org.bukkit.block.BlockState;
import org.bukkit.event.EventHandler; import org.bukkit.event.EventHandler;
import org.bukkit.event.EventPriority; import org.bukkit.event.EventPriority;
import org.bukkit.event.Listener; import org.bukkit.event.Listener;
import org.bukkit.event.world.ChunkUnloadEvent; import org.bukkit.event.world.ChunkUnloadEvent;
import org.bukkit.event.world.StructureGrowEvent; import org.bukkit.event.world.StructureGrowEvent;
import org.bukkit.event.world.WorldInitEvent;
import org.bukkit.event.world.WorldUnloadEvent; import org.bukkit.event.world.WorldUnloadEvent;
import java.io.File;
public class WorldListener implements Listener { public class WorldListener implements Listener {
private final mcMMO plugin; private final mcMMO plugin;

View File

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

View File

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

View File

@ -1,7 +1,8 @@
package com.gmail.nossr50.skills.acrobatics; package com.gmail.nossr50.skills.acrobatics;
import com.gmail.nossr50.config.experience.ExperienceConfig; import com.gmail.nossr50.config.experience.ExperienceConfig;
import com.gmail.nossr50.datatypes.LimitedSizeList; import com.gmail.nossr50.datatypes.BlockLocationHistory;
import com.gmail.nossr50.datatypes.experience.XPGainReason;
import com.gmail.nossr50.datatypes.interactions.NotificationType; import com.gmail.nossr50.datatypes.interactions.NotificationType;
import com.neetgames.mcmmo.player.OnlineMMOPlayer; import com.neetgames.mcmmo.player.OnlineMMOPlayer;
import com.gmail.nossr50.datatypes.skills.PrimarySkillType; import com.gmail.nossr50.datatypes.skills.PrimarySkillType;
@ -27,13 +28,13 @@ public class AcrobaticsManager extends SkillManager {
public AcrobaticsManager(OnlineMMOPlayer mmoPlayer) { public AcrobaticsManager(OnlineMMOPlayer mmoPlayer) {
super(mmoPlayer, PrimarySkillType.ACROBATICS); super(mmoPlayer, PrimarySkillType.ACROBATICS);
fallLocationMap = new LimitedSizeList(50); fallLocationMap = new BlockLocationHistory(50);
} }
private long rollXPCooldown = 0; private long rollXPCooldown = 0;
private final long rollXPInterval = (1000 * 3); //1 Minute private final long rollXPInterval = (1000 * 3); //1 Minute
private long rollXPIntervalLengthen = (1000 * 10); //10 Seconds private long rollXPIntervalLengthen = (1000 * 10); //10 Seconds
private final LimitedSizeList fallLocationMap; private final BlockLocationHistory fallLocationMap;
public boolean hasFallenInLocationBefore(Location location) public boolean hasFallenInLocationBefore(Location location)
{ {

View File

@ -17,6 +17,7 @@ import org.bukkit.entity.LivingEntity;
import org.bukkit.entity.Player; import org.bukkit.entity.Player;
import org.bukkit.event.entity.EntityDamageEvent.DamageModifier; import org.bukkit.event.entity.EntityDamageEvent.DamageModifier;
import org.bukkit.inventory.ItemStack; import org.bukkit.inventory.ItemStack;
import org.jetbrains.annotations.NotNull;
import java.util.Map; import java.util.Map;
@ -32,28 +33,28 @@ public class AxesManager extends SkillManager {
return Permissions.isSubSkillEnabled(getPlayer(), SubSkillType.AXES_AXE_MASTERY); return Permissions.isSubSkillEnabled(getPlayer(), SubSkillType.AXES_AXE_MASTERY);
} }
public boolean canCriticalHit(LivingEntity target) { public boolean canCriticalHit(@NotNull LivingEntity target) {
if(!RankUtils.hasUnlockedSubskill(getPlayer(), SubSkillType.AXES_CRITICAL_STRIKES)) if(!RankUtils.hasUnlockedSubskill(getPlayer(), SubSkillType.AXES_CRITICAL_STRIKES))
return false; return false;
return target.isValid() && Permissions.isSubSkillEnabled(getPlayer(), SubSkillType.AXES_CRITICAL_STRIKES); return target.isValid() && Permissions.isSubSkillEnabled(getPlayer(), SubSkillType.AXES_CRITICAL_STRIKES);
} }
public boolean canImpact(LivingEntity target) { public boolean canImpact(@NotNull LivingEntity target) {
if(!RankUtils.hasUnlockedSubskill(getPlayer(), SubSkillType.AXES_ARMOR_IMPACT)) if(!RankUtils.hasUnlockedSubskill(getPlayer(), SubSkillType.AXES_ARMOR_IMPACT))
return false; return false;
return target.isValid() && Permissions.isSubSkillEnabled(getPlayer(), SubSkillType.AXES_ARMOR_IMPACT) && Axes.hasArmor(target); return target.isValid() && Permissions.isSubSkillEnabled(getPlayer(), SubSkillType.AXES_ARMOR_IMPACT) && Axes.hasArmor(target);
} }
public boolean canGreaterImpact(LivingEntity target) { public boolean canGreaterImpact(@NotNull LivingEntity target) {
if(!RankUtils.hasUnlockedSubskill(getPlayer(), SubSkillType.AXES_GREATER_IMPACT)) if(!RankUtils.hasUnlockedSubskill(getPlayer(), SubSkillType.AXES_GREATER_IMPACT))
return false; return false;
return target.isValid() && Permissions.isSubSkillEnabled(getPlayer(), SubSkillType.AXES_GREATER_IMPACT) && !Axes.hasArmor(target); return target.isValid() && Permissions.isSubSkillEnabled(getPlayer(), SubSkillType.AXES_GREATER_IMPACT) && !Axes.hasArmor(target);
} }
public boolean canUseSkullSplitter(LivingEntity target) { public boolean canUseSkullSplitter(@NotNull LivingEntity target) {
if(!RankUtils.hasUnlockedSubskill(getPlayer(), SubSkillType.AXES_SKULL_SPLITTER)) if(!RankUtils.hasUnlockedSubskill(getPlayer(), SubSkillType.AXES_SKULL_SPLITTER))
return false; return false;
@ -68,7 +69,7 @@ public class AxesManager extends SkillManager {
* Handle the effects of the Axe Mastery ability * Handle the effects of the Axe Mastery ability
*/ */
public double axeMastery() { public double axeMastery() {
if (!RandomChanceUtil.isActivationSuccessful(SkillActivationType.ALWAYS_FIRES, SubSkillType.AXES_AXE_MASTERY, getPlayer(), mmoPlayer.getAttackStrength())) { if (!RandomChanceUtil.isActivationSuccessful(SkillActivationType.ALWAYS_FIRES, SubSkillType.AXES_AXE_MASTERY, getPlayer())) {
return 0; return 0;
} }
@ -82,7 +83,7 @@ public class AxesManager extends SkillManager {
* @param damage The amount of damage initially dealt by the event * @param damage The amount of damage initially dealt by the event
*/ */
public double criticalHit(LivingEntity target, double damage) { public double criticalHit(LivingEntity target, double damage) {
if (!RandomChanceUtil.isActivationSuccessful(SkillActivationType.RANDOM_LINEAR_100_SCALE_WITH_CAP, SubSkillType.AXES_CRITICAL_STRIKES, getPlayer(), mmoPlayer.getAttackStrength())) { if (!RandomChanceUtil.isActivationSuccessful(SkillActivationType.RANDOM_LINEAR_100_SCALE_WITH_CAP, SubSkillType.AXES_CRITICAL_STRIKES, getPlayer())) {
return 0; return 0;
} }
@ -113,12 +114,12 @@ public class AxesManager extends SkillManager {
* *
* @param target The {@link LivingEntity} being affected by Impact * @param target The {@link LivingEntity} being affected by Impact
*/ */
public void impactCheck(LivingEntity target) { public void impactCheck(@NotNull LivingEntity target) {
double durabilityDamage = getImpactDurabilityDamage(); double durabilityDamage = getImpactDurabilityDamage();
for (ItemStack armor : target.getEquipment().getArmorContents()) { for (ItemStack armor : target.getEquipment().getArmorContents()) {
if (armor != null && ItemUtils.isArmor(armor)) { if (armor != null && ItemUtils.isArmor(armor)) {
if (RandomChanceUtil.isActivationSuccessful(SkillActivationType.RANDOM_STATIC_CHANCE, SubSkillType.AXES_ARMOR_IMPACT, getPlayer(), mmoPlayer.getAttackStrength())) { if (RandomChanceUtil.isActivationSuccessful(SkillActivationType.RANDOM_STATIC_CHANCE, SubSkillType.AXES_ARMOR_IMPACT, getPlayer())) {
SkillUtils.handleDurabilityChange(armor, durabilityDamage, 1); SkillUtils.handleDurabilityChange(armor, durabilityDamage, 1);
} }
} }
@ -134,9 +135,9 @@ public class AxesManager extends SkillManager {
* *
* @param target The {@link LivingEntity} being affected by the ability * @param target The {@link LivingEntity} being affected by the ability
*/ */
public double greaterImpact(LivingEntity target) { public double greaterImpact(@NotNull LivingEntity target) {
//static chance (3rd param) //static chance (3rd param)
if (!RandomChanceUtil.isActivationSuccessful(SkillActivationType.RANDOM_STATIC_CHANCE, SubSkillType.AXES_GREATER_IMPACT, getPlayer(), mmoPlayer.getAttackStrength())) { if (!RandomChanceUtil.isActivationSuccessful(SkillActivationType.RANDOM_STATIC_CHANCE, SubSkillType.AXES_GREATER_IMPACT, getPlayer())) {
return 0; return 0;
} }
@ -166,7 +167,7 @@ public class AxesManager extends SkillManager {
* @param target The {@link LivingEntity} being affected by the ability * @param target The {@link LivingEntity} being affected by the ability
* @param damage The amount of damage initially dealt by the event * @param damage The amount of damage initially dealt by the event
*/ */
public void skullSplitterCheck(LivingEntity target, double damage, Map<DamageModifier, Double> modifiers) { public void skullSplitterCheck(@NotNull LivingEntity target, double damage, Map<DamageModifier, Double> modifiers) {
CombatUtils.applyAbilityAoE(getPlayer(), target, damage / Axes.skullSplitterModifier, modifiers, skill); CombatUtils.applyAbilityAoE(getPlayer(), target, damage / Axes.skullSplitterModifier, modifiers, skill);
} }
} }

View File

@ -738,7 +738,6 @@ public class HerbalismManager extends SkillManager {
return false; return false;
} }
if (!playerInventory.containsAtLeast(seedStack, 1)) { if (!playerInventory.containsAtLeast(seedStack, 1)) {
return false; return false;
} }

View File

@ -62,7 +62,7 @@ public class SwordsManager extends SkillManager {
*/ */
public void ruptureCheck(@NotNull LivingEntity target) throws IllegalStateException { public void ruptureCheck(@NotNull LivingEntity target) throws IllegalStateException {
if(BleedTimerTask.isBleedOperationAllowed()) { if(BleedTimerTask.isBleedOperationAllowed()) {
if (RandomChanceUtil.isActivationSuccessful(SkillActivationType.RANDOM_LINEAR_100_SCALE_WITH_CAP, SubSkillType.SWORDS_RUPTURE, getPlayer(), this.mmoPlayer.getAttackStrength())) { if (RandomChanceUtil.isActivationSuccessful(SkillActivationType.RANDOM_LINEAR_100_SCALE_WITH_CAP, SubSkillType.SWORDS_RUPTURE, getPlayer())) {
if (target instanceof Player) { if (target instanceof Player) {
Player defender = (Player) target; Player defender = (Player) target;
@ -98,7 +98,7 @@ public class SwordsManager extends SkillManager {
return 0; return 0;
} }
public int getToolTier(ItemStack itemStack) public int getToolTier(@NotNull ItemStack itemStack)
{ {
if(ItemUtils.isNetheriteTool(itemStack)) if(ItemUtils.isNetheriteTool(itemStack))
return 5; return 5;
@ -128,7 +128,7 @@ public class SwordsManager extends SkillManager {
* @param attacker The {@link LivingEntity} being affected by the ability * @param attacker The {@link LivingEntity} being affected by the ability
* @param damage The amount of damage initially dealt by the event * @param damage The amount of damage initially dealt by the event
*/ */
public void counterAttackChecks(LivingEntity attacker, double damage) { public void counterAttackChecks(@NotNull LivingEntity attacker, double damage) {
if (RandomChanceUtil.isActivationSuccessful(SkillActivationType.RANDOM_LINEAR_100_SCALE_WITH_CAP, SubSkillType.SWORDS_COUNTER_ATTACK, getPlayer())) { if (RandomChanceUtil.isActivationSuccessful(SkillActivationType.RANDOM_LINEAR_100_SCALE_WITH_CAP, SubSkillType.SWORDS_COUNTER_ATTACK, getPlayer())) {
CombatUtils.dealDamage(attacker, damage / Swords.counterAttackModifier, getPlayer()); CombatUtils.dealDamage(attacker, damage / Swords.counterAttackModifier, getPlayer());
@ -146,7 +146,7 @@ public class SwordsManager extends SkillManager {
* @param target The {@link LivingEntity} being affected by the ability * @param target The {@link LivingEntity} being affected by the ability
* @param damage The amount of damage initially dealt by the event * @param damage The amount of damage initially dealt by the event
*/ */
public void serratedStrikes(LivingEntity target, double damage, Map<DamageModifier, Double> modifiers) { public void serratedStrikes(@NotNull LivingEntity target, double damage, Map<DamageModifier, Double> modifiers) {
CombatUtils.applyAbilityAoE(getPlayer(), target, damage / Swords.serratedStrikesModifier, modifiers, skill); CombatUtils.applyAbilityAoE(getPlayer(), target, damage / Swords.serratedStrikesModifier, modifiers, skill);
} }
} }

View File

@ -24,6 +24,7 @@ import org.bukkit.entity.Item;
import org.bukkit.entity.LivingEntity; import org.bukkit.entity.LivingEntity;
import org.bukkit.entity.Player; import org.bukkit.entity.Player;
import org.bukkit.inventory.ItemStack; import org.bukkit.inventory.ItemStack;
import org.jetbrains.annotations.NotNull;
public class UnarmedManager extends SkillManager { public class UnarmedManager extends SkillManager {
@ -69,7 +70,7 @@ public class UnarmedManager extends SkillManager {
return Permissions.isSubSkillEnabled(getPlayer(), SubSkillType.UNARMED_BLOCK_CRACKER); return Permissions.isSubSkillEnabled(getPlayer(), SubSkillType.UNARMED_BLOCK_CRACKER);
} }
public boolean blockCrackerCheck(BlockState blockState) { public boolean blockCrackerCheck(@NotNull BlockState blockState) {
if (!RandomChanceUtil.isActivationSuccessful(SkillActivationType.ALWAYS_FIRES, SubSkillType.UNARMED_BLOCK_CRACKER, getPlayer())) { if (!RandomChanceUtil.isActivationSuccessful(SkillActivationType.ALWAYS_FIRES, SubSkillType.UNARMED_BLOCK_CRACKER, getPlayer())) {
return false; return false;
} }
@ -100,8 +101,8 @@ public class UnarmedManager extends SkillManager {
* *
* @param defender The defending player * @param defender The defending player
*/ */
public void disarmCheck(Player defender) { public void disarmCheck(@NotNull Player defender) {
if (RandomChanceUtil.isActivationSuccessful(SkillActivationType.RANDOM_LINEAR_100_SCALE_WITH_CAP, SubSkillType.UNARMED_DISARM, getPlayer(), mmoPlayer.getAttackStrength()) && !hasIronGrip(defender)) { if (RandomChanceUtil.isActivationSuccessful(SkillActivationType.RANDOM_LINEAR_100_SCALE_WITH_CAP, SubSkillType.UNARMED_DISARM, getPlayer()) && !hasIronGrip(defender)) {
if (EventUtils.callDisarmEvent(defender).isCancelled()) { if (EventUtils.callDisarmEvent(defender).isCancelled()) {
return; return;
} }
@ -178,7 +179,7 @@ public class UnarmedManager extends SkillManager {
* @param defender The defending player * @param defender The defending player
* @return true if the defender was not disarmed, false otherwise * @return true if the defender was not disarmed, false otherwise
*/ */
private boolean hasIronGrip(Player defender) { private boolean hasIronGrip(@NotNull Player defender) {
if (!Misc.isNPCEntityExcludingVillagers(defender) if (!Misc.isNPCEntityExcludingVillagers(defender)
&& Permissions.isSubSkillEnabled(defender, SubSkillType.UNARMED_IRON_GRIP) && Permissions.isSubSkillEnabled(defender, SubSkillType.UNARMED_IRON_GRIP)
&& RandomChanceUtil.isActivationSuccessful(SkillActivationType.RANDOM_LINEAR_100_SCALE_WITH_CAP, SubSkillType.UNARMED_IRON_GRIP, defender)) { && RandomChanceUtil.isActivationSuccessful(SkillActivationType.RANDOM_LINEAR_100_SCALE_WITH_CAP, SubSkillType.UNARMED_IRON_GRIP, defender)) {

View File

@ -40,7 +40,7 @@ public class HashChunkManager implements ChunkManager {
} }
private synchronized @Nullable ChunkStore readChunkStore(@NotNull World world, int cx, int cz) throws IOException { private synchronized @Nullable ChunkStore readChunkStore(@NotNull World world, int cx, int cz) throws IOException {
McMMOSimpleRegionFile rf = getSimpleRegionFile(world, cx, cz, false); McMMOSimpleRegionFile rf = getReadableSimpleRegionFile(world, cx, cz);
if (rf == null) if (rf == null)
return null; // If there is no region file, there can't be a chunk return null; // If there is no region file, there can't be a chunk
try (DataInputStream in = rf.getInputStream(cx, cz)) { // Get input stream for chunk try (DataInputStream in = rf.getInputStream(cx, cz)) { // Get input stream for chunk
@ -54,7 +54,7 @@ public class HashChunkManager implements ChunkManager {
if (!data.isDirty()) if (!data.isDirty())
return; // Don't save unchanged data return; // Don't save unchanged data
try { try {
McMMOSimpleRegionFile rf = getSimpleRegionFile(world, data.getChunkX(), data.getChunkZ(), true); McMMOSimpleRegionFile rf = getWriteableSimpleRegionFile(world, data.getChunkX(), data.getChunkZ());
try (DataOutputStream out = rf.getOutputStream(data.getChunkX(), data.getChunkZ())) { try (DataOutputStream out = rf.getOutputStream(data.getChunkX(), data.getChunkZ())) {
BitSetChunkStore.Serialization.writeChunkStore(out, data); BitSetChunkStore.Serialization.writeChunkStore(out, data);
} }
@ -65,21 +65,33 @@ public class HashChunkManager implements ChunkManager {
} }
} }
private synchronized @Nullable McMMOSimpleRegionFile getSimpleRegionFile(@NotNull World world, int cx, int cz, boolean createIfAbsent) { private synchronized @NotNull McMMOSimpleRegionFile getWriteableSimpleRegionFile(@NotNull World world, int cx, int cz) {
CoordinateKey regionKey = toRegionKey(world.getUID(), cx, cz); CoordinateKey regionKey = toRegionKey(world.getUID(), cx, cz);
return regionMap.computeIfAbsent(regionKey, k -> { return regionMap.computeIfAbsent(regionKey, k -> {
File worldRegionsDirectory = new File(world.getWorldFolder(), "mcmmo_regions"); File regionFile = getRegionFile(world, regionKey);
if (!createIfAbsent && !worldRegionsDirectory.isDirectory()) regionFile.getParentFile().mkdirs();
return null; // Don't create the directory on read-only operations return new McMMOSimpleRegionFile(regionFile, regionKey.x, regionKey.z);
worldRegionsDirectory.mkdirs(); // Ensure directory exists });
File regionFile = new File(worldRegionsDirectory, "mcmmo_" + regionKey.x + "_" + regionKey.z + "_.mcm"); }
if (!createIfAbsent && !regionFile.exists())
private synchronized @Nullable McMMOSimpleRegionFile getReadableSimpleRegionFile(@NotNull World world, int cx, int cz) {
CoordinateKey regionKey = toRegionKey(world.getUID(), cx, cz);
return regionMap.computeIfAbsent(regionKey, k -> {
File regionFile = getRegionFile(world, regionKey);
if (!regionFile.exists())
return null; // Don't create the file on read-only operations return null; // Don't create the file on read-only operations
return new McMMOSimpleRegionFile(regionFile, regionKey.x, regionKey.z); return new McMMOSimpleRegionFile(regionFile, regionKey.x, regionKey.z);
}); });
} }
private @NotNull File getRegionFile(@NotNull World world, @NotNull CoordinateKey regionKey) {
if (world.getUID() != regionKey.worldID)
throw new IllegalArgumentException();
return new File(new File(world.getWorldFolder(), "mcmmo_regions"), "mcmmo_" + regionKey.x + "_" + regionKey.z + "_.mcm");
}
private @Nullable ChunkStore loadChunk(int cx, int cz, @NotNull World world) { private @Nullable ChunkStore loadChunk(int cx, int cz, @NotNull World world) {
try { try {
return readChunkStore(world, cx, cz); return readChunkStore(world, cx, cz);
@ -228,15 +240,15 @@ public class HashChunkManager implements ChunkManager {
cStore.set(ix, y, iz, value); cStore.set(ix, y, iz, value);
} }
private CoordinateKey blockCoordinateToChunkKey(@NotNull UUID worldUid, int x, int y, int z) { private @NotNull CoordinateKey blockCoordinateToChunkKey(@NotNull UUID worldUid, int x, int y, int z) {
return toChunkKey(worldUid, x >> 4, z >> 4); return toChunkKey(worldUid, x >> 4, z >> 4);
} }
private CoordinateKey toChunkKey(@NotNull UUID worldUid, int cx, int cz){ private @NotNull CoordinateKey toChunkKey(@NotNull UUID worldUid, int cx, int cz){
return new CoordinateKey(worldUid, cx, cz); return new CoordinateKey(worldUid, cx, cz);
} }
private CoordinateKey toRegionKey(@NotNull UUID worldUid, int cx, int cz) { private @NotNull CoordinateKey toRegionKey(@NotNull UUID worldUid, int cx, int cz) {
// Compute region index (32x32 chunk regions) // Compute region index (32x32 chunk regions)
int rx = cx >> 5; int rx = cx >> 5;
int rz = cz >> 5; int rz = cz >> 5;

View File

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

View File

@ -3,6 +3,7 @@ package com.gmail.nossr50.util.random;
public interface RandomChanceExecution { public interface RandomChanceExecution {
/** /**
* Gets the XPos used in the formula for success * Gets the XPos used in the formula for success
*
* @return value of x for our success probability graph * @return value of x for our success probability graph
*/ */
double getXPos(); double getXPos();
@ -10,6 +11,7 @@ public interface RandomChanceExecution {
/** /**
* The maximum odds for this RandomChanceExecution * 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 * 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) * @return maximum probability odds from 0.00 (no chance of ever happened) to 100.0 (probability can be guaranteed)
*/ */
double getProbabilityCap(); double getProbabilityCap();

View File

@ -1,132 +1,114 @@
package com.gmail.nossr50.util.random; package com.gmail.nossr50.util.random;
import com.gmail.nossr50.config.AdvancedConfig; import com.gmail.nossr50.datatypes.player.McMMOPlayer;
import com.neetgames.mcmmo.player.OnlineMMOPlayer;
import com.gmail.nossr50.datatypes.skills.PrimarySkillType;
import com.gmail.nossr50.datatypes.skills.SubSkillType; import com.gmail.nossr50.datatypes.skills.SubSkillType;
import com.gmail.nossr50.util.Permissions; import com.gmail.nossr50.util.Permissions;
import com.gmail.nossr50.util.player.UserManager;
import org.bukkit.entity.Player; import org.bukkit.entity.Player;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
public class RandomChanceSkill implements RandomChanceExecution { public class RandomChanceSkill implements RandomChanceExecution {
protected final PrimarySkillType primarySkillType;
protected final SubSkillType subSkillType;
protected final double probabilityCap; protected final double probabilityCap;
protected final boolean isLucky; protected final boolean isLucky;
protected int skillLevel; protected int skillLevel;
protected double resultModifier; protected final double resultModifier;
protected final double maximumBonusLevelCap;
public RandomChanceSkill(Player player, SubSkillType subSkillType, double resultModifier) public RandomChanceSkill(@Nullable Player player, @NotNull SubSkillType subSkillType, double resultModifier) {
{
this.primarySkillType = subSkillType.getParentSkill();
this.subSkillType = subSkillType;
this.probabilityCap = RandomChanceUtil.LINEAR_CURVE_VAR; this.probabilityCap = RandomChanceUtil.LINEAR_CURVE_VAR;
final OnlineMMOPlayer mmoPlayer = mcMMO.getUserManager().getPlayer(player); final McMMOPlayer mcMMOPlayer = UserManager.getPlayer(player);
if (player != null && mmoPlayer != null) { if (player != null && mcMMOPlayer != null) {
this.skillLevel = mmoPlayer.getSkillLevel(primarySkillType); this.skillLevel = mcMMOPlayer.getSkillLevel(subSkillType.getParentSkill());
} else { } else {
this.skillLevel = 0; this.skillLevel = 0;
} }
if (player != null) if (player != null)
isLucky = Permissions.lucky(player, primarySkillType); isLucky = Permissions.lucky(player, subSkillType.getParentSkill());
else else
isLucky = false; isLucky = false;
this.resultModifier = resultModifier; this.resultModifier = resultModifier;
this.maximumBonusLevelCap = RandomChanceUtil.getMaxBonusLevelCap(subSkillType);
} }
public RandomChanceSkill(Player player, SubSkillType subSkillType) public RandomChanceSkill(@Nullable Player player, @NotNull SubSkillType subSkillType) {
{
this.primarySkillType = subSkillType.getParentSkill();
this.subSkillType = subSkillType;
this.probabilityCap = RandomChanceUtil.LINEAR_CURVE_VAR; this.probabilityCap = RandomChanceUtil.LINEAR_CURVE_VAR;
final OnlineMMOPlayer mmoPlayer = mcMMO.getUserManager().getPlayer(player); final McMMOPlayer mcMMOPlayer = UserManager.getPlayer(player);
if (player != null && mmoPlayer != null) { if (player != null && mcMMOPlayer != null) {
this.skillLevel = mmoPlayer.getSkillLevel(primarySkillType); this.skillLevel = mcMMOPlayer.getSkillLevel(subSkillType.getParentSkill());
} else { } else {
this.skillLevel = 0; this.skillLevel = 0;
} }
if (player != null) if (player != null)
isLucky = Permissions.lucky(player, primarySkillType); isLucky = Permissions.lucky(player, subSkillType.getParentSkill());
else else
isLucky = false; isLucky = false;
this.resultModifier = 1.0D; this.resultModifier = 1.0D;
this.maximumBonusLevelCap = RandomChanceUtil.getMaxBonusLevelCap(subSkillType);
} }
public RandomChanceSkill(Player player, SubSkillType subSkillType, boolean hasCap) public RandomChanceSkill(@Nullable Player player, @NotNull SubSkillType subSkillType, boolean hasCap) {
{
if (hasCap) if (hasCap)
this.probabilityCap = AdvancedConfig.getInstance().getMaximumProbability(subSkillType); this.probabilityCap = RandomChanceUtil.getMaximumProbability(subSkillType);
else else
this.probabilityCap = RandomChanceUtil.LINEAR_CURVE_VAR; this.probabilityCap = RandomChanceUtil.LINEAR_CURVE_VAR;
this.primarySkillType = subSkillType.getParentSkill(); final McMMOPlayer mcMMOPlayer = UserManager.getPlayer(player);
this.subSkillType = subSkillType; if (player != null && mcMMOPlayer != null) {
this.skillLevel = mcMMOPlayer.getSkillLevel(subSkillType.getParentSkill());
final OnlineMMOPlayer mmoPlayer = mcMMO.getUserManager().getPlayer(player);
if (player != null && mmoPlayer != null) {
this.skillLevel = mmoPlayer.getSkillLevel(primarySkillType);
} else { } else {
this.skillLevel = 0; this.skillLevel = 0;
} }
if (player != null) if (player != null)
isLucky = Permissions.lucky(player, primarySkillType); isLucky = Permissions.lucky(player, subSkillType.getParentSkill());
else else
isLucky = false; isLucky = false;
this.resultModifier = 1.0D; this.resultModifier = 1.0D;
this.maximumBonusLevelCap = RandomChanceUtil.getMaxBonusLevelCap(subSkillType);
} }
public RandomChanceSkill(Player player, SubSkillType subSkillType, boolean hasCap, double resultModifier) public RandomChanceSkill(@Nullable Player player, @NotNull SubSkillType subSkillType, boolean hasCap, double resultModifier) {
{
if (hasCap) if (hasCap)
this.probabilityCap = AdvancedConfig.getInstance().getMaximumProbability(subSkillType); this.probabilityCap = RandomChanceUtil.getMaximumProbability(subSkillType);
else else
this.probabilityCap = RandomChanceUtil.LINEAR_CURVE_VAR; this.probabilityCap = RandomChanceUtil.LINEAR_CURVE_VAR;
this.primarySkillType = subSkillType.getParentSkill(); final McMMOPlayer mcMMOPlayer = UserManager.getPlayer(player);
this.subSkillType = subSkillType; if (player != null && mcMMOPlayer != null) {
this.skillLevel = mcMMOPlayer.getSkillLevel(subSkillType.getParentSkill());
final OnlineMMOPlayer mmoPlayer = mcMMO.getUserManager().getPlayer(player);
if (player != null && mmoPlayer != null) {
this.skillLevel = mmoPlayer.getSkillLevel(primarySkillType);
} else { } else {
this.skillLevel = 0; this.skillLevel = 0;
} }
if (player != null) if (player != null)
isLucky = Permissions.lucky(player, primarySkillType); isLucky = Permissions.lucky(player, subSkillType.getParentSkill());
else else
isLucky = false; isLucky = false;
this.resultModifier = resultModifier; this.resultModifier = resultModifier;
} this.maximumBonusLevelCap = RandomChanceUtil.getMaxBonusLevelCap(subSkillType);
/**
* The subskill corresponding to this RandomChanceSkill
* @return this subskill
*/
public SubSkillType getSubSkill() {
return subSkillType;
} }
/** /**
* Gets the skill level of the player who owns this RandomChanceSkill * Gets the skill level of the player who owns this RandomChanceSkill
*
* @return the current skill level relating to this RandomChanceSkill * @return the current skill level relating to this RandomChanceSkill
*/ */
public int getSkillLevel() public int getSkillLevel() {
{
return skillLevel; return skillLevel;
} }
/** /**
* Modify the skill level used for this skill's RNG calculations * Modify the skill level used for this skill's RNG calculations
*
* @param newSkillLevel new skill level * @param newSkillLevel new skill level
*/ */
public void setSkillLevel(int newSkillLevel) { public void setSkillLevel(int newSkillLevel) {
@ -141,7 +123,7 @@ public class RandomChanceSkill implements RandomChanceExecution {
* @return the maximum bonus from skill level for this skill * @return the maximum bonus from skill level for this skill
*/ */
public double getMaximumBonusLevelCap() { public double getMaximumBonusLevelCap() {
return AdvancedConfig.getInstance().getMaxBonusLevel(subSkillType); return maximumBonusLevelCap;
} }
/** /**
@ -172,8 +154,4 @@ public class RandomChanceSkill implements RandomChanceExecution {
public double getResultModifier() { public double getResultModifier() {
return resultModifier; return resultModifier;
} }
public void setResultModifier(double resultModifier) {
this.resultModifier = resultModifier;
}
} }

View File

@ -2,19 +2,19 @@ package com.gmail.nossr50.util.random;
import com.gmail.nossr50.datatypes.skills.SubSkillType; import com.gmail.nossr50.datatypes.skills.SubSkillType;
import org.bukkit.entity.Player; import org.bukkit.entity.Player;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
public class RandomChanceSkillStatic extends RandomChanceSkill { public class RandomChanceSkillStatic extends RandomChanceSkill {
private final double xPos; private final double xPos;
public RandomChanceSkillStatic(double xPos, Player player, SubSkillType subSkillType) public RandomChanceSkillStatic(double xPos, @Nullable Player player, @NotNull SubSkillType subSkillType) {
{
super(player, subSkillType); super(player, subSkillType);
this.xPos = xPos; this.xPos = xPos;
} }
public RandomChanceSkillStatic(double xPos, Player player, SubSkillType subSkillType, double resultModifier) public RandomChanceSkillStatic(double xPos, @Nullable Player player, @NotNull SubSkillType subSkillType, double resultModifier) {
{
super(player, subSkillType, resultModifier); super(player, subSkillType, resultModifier);
this.xPos = xPos; this.xPos = xPos;

View File

@ -5,8 +5,7 @@ public class RandomChanceStatic implements RandomChanceExecution {
private final double probabilityCap; private final double probabilityCap;
private final boolean isLucky; private final boolean isLucky;
public RandomChanceStatic(double xPos, boolean isLucky) public RandomChanceStatic(double xPos, boolean isLucky) {
{
this.xPos = xPos; this.xPos = xPos;
this.probabilityCap = xPos; this.probabilityCap = xPos;
this.isLucky = isLucky; this.isLucky = isLucky;

View File

@ -1,10 +1,8 @@
package com.gmail.nossr50.util.random; package com.gmail.nossr50.util.random;
import com.gmail.nossr50.config.AdvancedConfig; import com.gmail.nossr50.config.AdvancedConfig;
import com.neetgames.mcmmo.player.OnlineMMOPlayer;
import com.gmail.nossr50.datatypes.skills.PrimarySkillType; import com.gmail.nossr50.datatypes.skills.PrimarySkillType;
import com.gmail.nossr50.datatypes.skills.SubSkillType; import com.gmail.nossr50.datatypes.skills.SubSkillType;
import com.gmail.nossr50.datatypes.skills.subskills.AbstractSubSkill;
import com.gmail.nossr50.events.skills.secondaryabilities.SubSkillEvent; import com.gmail.nossr50.events.skills.secondaryabilities.SubSkillEvent;
import com.gmail.nossr50.events.skills.secondaryabilities.SubSkillRandomCheckEvent; import com.gmail.nossr50.events.skills.secondaryabilities.SubSkillRandomCheckEvent;
import com.gmail.nossr50.util.EventUtils; import com.gmail.nossr50.util.EventUtils;
@ -12,15 +10,16 @@ import com.gmail.nossr50.util.Permissions;
import com.gmail.nossr50.util.skills.SkillActivationType; import com.gmail.nossr50.util.skills.SkillActivationType;
import org.bukkit.entity.Player; import org.bukkit.entity.Player;
import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.text.DecimalFormat; import java.text.DecimalFormat;
import java.util.concurrent.ThreadLocalRandom; import java.util.concurrent.ThreadLocalRandom;
public class RandomChanceUtil public class RandomChanceUtil {
{ public static final @NotNull DecimalFormat percent = new DecimalFormat("##0.00%");
public static final DecimalFormat percent = new DecimalFormat("##0.00%");
//public static final DecimalFormat decimal = 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 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 * This method is the final step in determining if a Sub-Skill / Secondary Skill in mcMMO successfully activates either from chance or otherwise
@ -32,10 +31,8 @@ public class RandomChanceUtil
* @param player The owner of this sub-skill * @param player The owner of this sub-skill
* @return returns true if all conditions are met and the event is not cancelled * @return returns true if all conditions are met and the event is not cancelled
*/ */
public static boolean isActivationSuccessful(SkillActivationType skillActivationType, SubSkillType subSkillType, Player player) public static boolean isActivationSuccessful(@NotNull SkillActivationType skillActivationType, @NotNull SubSkillType subSkillType, @Nullable Player player) {
{ switch (skillActivationType) {
switch(skillActivationType)
{
case RANDOM_LINEAR_100_SCALE_WITH_CAP: case RANDOM_LINEAR_100_SCALE_WITH_CAP:
return checkRandomChanceExecutionSuccess(player, subSkillType, true); return checkRandomChanceExecutionSuccess(player, subSkillType, true);
case RANDOM_STATIC_CHANCE: case RANDOM_STATIC_CHANCE:
@ -48,36 +45,8 @@ public class RandomChanceUtil
} }
} }
/** public static double getActivationChance(@NotNull SkillActivationType skillActivationType, @NotNull SubSkillType subSkillType, @Nullable Player player) {
* This method is the final step in determining if a Sub-Skill / Secondary Skill in mcMMO successfully activates either from chance or otherwise switch (skillActivationType) {
* 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
*
* @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
*/
public static boolean isActivationSuccessful(SkillActivationType skillActivationType, SubSkillType subSkillType, Player player, double resultModifier)
{
switch(skillActivationType)
{
case RANDOM_LINEAR_100_SCALE_WITH_CAP:
return checkRandomChanceExecutionSuccess(player, subSkillType, true);
case RANDOM_STATIC_CHANCE:
return checkRandomStaticChanceExecutionSuccess(player, subSkillType, resultModifier);
case ALWAYS_FIRES:
SubSkillEvent event = EventUtils.callSubSkillEvent(player, subSkillType);
return !event.isCancelled();
default:
return false;
}
}
public static double getActivationChance(SkillActivationType skillActivationType, SubSkillType subSkillType, Player player)
{
switch(skillActivationType)
{
case RANDOM_LINEAR_100_SCALE_WITH_CAP: case RANDOM_LINEAR_100_SCALE_WITH_CAP:
return getRandomChanceExecutionSuccess(player, subSkillType, true); return getRandomChanceExecutionSuccess(player, subSkillType, true);
case RANDOM_STATIC_CHANCE: case RANDOM_STATIC_CHANCE:
@ -89,10 +58,10 @@ public class RandomChanceUtil
/** /**
* Checks whether or not the random chance succeeds * Checks whether or not the random chance succeeds
*
* @return true if the random chance succeeds * @return true if the random chance succeeds
*/ */
public static boolean checkRandomChanceExecutionSuccess(Player player, PrimarySkillType primarySkillType, double chance) public static boolean checkRandomChanceExecutionSuccess(@NotNull Player player, @NotNull PrimarySkillType primarySkillType, double chance) {
{
//Check the odds //Check the odds
chance *= 100; chance *= 100;
@ -115,11 +84,11 @@ public class RandomChanceUtil
/** /**
* Used for stuff like Excavation, Fishing, etc... * Used for stuff like Excavation, Fishing, etc...
*
* @param randomChance * @param randomChance
* @return * @return
*/ */
public static boolean checkRandomChanceExecutionSuccess(RandomChanceSkillStatic randomChance, double resultModifier) public static boolean checkRandomChanceExecutionSuccess(@NotNull RandomChanceSkillStatic randomChance, double resultModifier) {
{
double chanceOfSuccess = calculateChanceOfSuccess(randomChance); double chanceOfSuccess = calculateChanceOfSuccess(randomChance);
//Check the odds //Check the odds
@ -128,16 +97,15 @@ public class RandomChanceUtil
/** /**
* Used for stuff like Excavation, Fishing, etc... * Used for stuff like Excavation, Fishing, etc...
*
* @param randomChance * @param randomChance
* @return * @return
*/ */
public static boolean checkRandomChanceExecutionSuccess(RandomChanceSkillStatic randomChance) public static boolean checkRandomChanceExecutionSuccess(@NotNull RandomChanceSkillStatic randomChance) {
{
return checkRandomChanceExecutionSuccess(randomChance, 1.0F); return checkRandomChanceExecutionSuccess(randomChance, 1.0F);
} }
public static boolean checkRandomChanceExecutionSuccess(RandomChanceSkill randomChance) public static boolean checkRandomChanceExecutionSuccess(@NotNull RandomChanceSkill randomChance) {
{
double chanceOfSuccess = calculateChanceOfSuccess(randomChance); double chanceOfSuccess = calculateChanceOfSuccess(randomChance);
//Check the odds //Check the odds
@ -153,14 +121,15 @@ public class RandomChanceUtil
/** /**
* Gets the Static Chance for something to activate * Gets the Static Chance for something to activate
*
* @param randomChance * @param randomChance
* @return * @return
*/ */
public static double getRandomChanceExecutionChance(RandomChanceExecution randomChance) { public static double getRandomChanceExecutionChance(@NotNull RandomChanceExecution randomChance) {
return getChanceOfSuccess(randomChance.getXPos(), randomChance.getProbabilityCap(), LINEAR_CURVE_VAR); return getChanceOfSuccess(randomChance.getXPos(), randomChance.getProbabilityCap(), LINEAR_CURVE_VAR);
} }
public static double getRandomChanceExecutionChance(RandomChanceStatic randomChance) { public static double getRandomChanceExecutionChance(@NotNull RandomChanceStatic randomChance) {
double chanceOfSuccess = getChanceOfSuccess(randomChance.getXPos(), randomChance.getProbabilityCap(), LINEAR_CURVE_VAR); double chanceOfSuccess = getChanceOfSuccess(randomChance.getXPos(), randomChance.getProbabilityCap(), LINEAR_CURVE_VAR);
chanceOfSuccess = addLuck(randomChance.isLucky(), chanceOfSuccess); chanceOfSuccess = addLuck(randomChance.isLucky(), chanceOfSuccess);
@ -173,7 +142,7 @@ public class RandomChanceUtil
return chanceOfSuccess; return chanceOfSuccess;
}*/ }*/
private static double calculateChanceOfSuccess(RandomChanceSkill randomChance) { public static double calculateChanceOfSuccess(@NotNull RandomChanceSkill randomChance) {
double skillLevel = randomChance.getSkillLevel(); double skillLevel = randomChance.getSkillLevel();
double maximumProbability = randomChance.getProbabilityCap(); double maximumProbability = randomChance.getProbabilityCap();
double maximumBonusLevel = randomChance.getMaximumBonusLevelCap(); double maximumBonusLevel = randomChance.getMaximumBonusLevelCap();
@ -194,7 +163,7 @@ public class RandomChanceUtil
return chanceOfSuccess; return chanceOfSuccess;
} }
private static double calculateChanceOfSuccess(RandomChanceSkillStatic randomChance) { public static double calculateChanceOfSuccess(@NotNull RandomChanceSkillStatic randomChance) {
double chanceOfSuccess = getChanceOfSuccess(randomChance.getXPos(), 100, 100); double chanceOfSuccess = getChanceOfSuccess(randomChance.getXPos(), 100, 100);
//Add Luck //Add Luck
@ -209,27 +178,23 @@ public class RandomChanceUtil
* *
* @return the chance of success from 0-100 (100 = guaranteed) * @return the chance of success from 0-100 (100 = guaranteed)
*/ */
private static int getChanceOfSuccess(double skillLevel, double maxProbability, double maxLevel) private static int getChanceOfSuccess(double skillLevel, double maxProbability, double maxLevel) {
{
//return (int) (x / (y / LINEAR_CURVE_VAR)); //return (int) (x / (y / LINEAR_CURVE_VAR));
return (int) (maxProbability * (skillLevel / maxLevel)); return (int) (maxProbability * (skillLevel / maxLevel));
// max probability * (weight/maxlevel) = chance of success // max probability * (weight/maxlevel) = chance of success
} }
private static int getChanceOfSuccess(double x, double y) private static int getChanceOfSuccess(double x, double y) {
{
return (int) (x / (y / LINEAR_CURVE_VAR)); return (int) (x / (y / LINEAR_CURVE_VAR));
// max probability * (weight/maxlevel) = chance of success // max probability * (weight/maxlevel) = chance of success
} }
public static double getRandomChanceExecutionSuccess(Player player, SubSkillType subSkillType, boolean hasCap) public static double getRandomChanceExecutionSuccess(@Nullable Player player, @NotNull SubSkillType subSkillType, boolean hasCap) {
{
RandomChanceSkill rcs = new RandomChanceSkill(player, subSkillType, hasCap); RandomChanceSkill rcs = new RandomChanceSkill(player, subSkillType, hasCap);
return calculateChanceOfSuccess(rcs); return calculateChanceOfSuccess(rcs);
} }
public static double getRandomStaticChanceExecutionSuccess(Player player, SubSkillType subSkillType) public static double getRandomStaticChanceExecutionSuccess(@Nullable Player player, @NotNull SubSkillType subSkillType) {
{
try { try {
return getRandomChanceExecutionChance(new RandomChanceSkillStatic(getStaticRandomChance(subSkillType), player, subSkillType)); return getRandomChanceExecutionChance(new RandomChanceSkillStatic(getStaticRandomChance(subSkillType), player, subSkillType));
} catch (InvalidStaticChance invalidStaticChance) { } catch (InvalidStaticChance invalidStaticChance) {
@ -240,29 +205,24 @@ public class RandomChanceUtil
return 0.1337; //Puts on shades return 0.1337; //Puts on shades
} }
public static boolean checkRandomChanceExecutionSuccess(Player player, SubSkillType subSkillType, boolean hasCap) public static boolean checkRandomChanceExecutionSuccess(@Nullable Player player, @NotNull SubSkillType subSkillType, boolean hasCap) {
{
return checkRandomChanceExecutionSuccess(new RandomChanceSkill(player, subSkillType, hasCap)); return checkRandomChanceExecutionSuccess(new RandomChanceSkill(player, subSkillType, hasCap));
} }
public static boolean checkRandomChanceExecutionSuccess(Player player, SubSkillType subSkillType) public static boolean checkRandomChanceExecutionSuccess(@Nullable Player player, @NotNull SubSkillType subSkillType) {
{
return checkRandomChanceExecutionSuccess(new RandomChanceSkill(player, subSkillType)); return checkRandomChanceExecutionSuccess(new RandomChanceSkill(player, subSkillType));
} }
public static boolean checkRandomChanceExecutionSuccess(Player player, SubSkillType subSkillType, boolean hasCap, double resultModifier) public static boolean checkRandomChanceExecutionSuccess(@Nullable Player player, @NotNull SubSkillType subSkillType, boolean hasCap, double resultModifier) {
{
return checkRandomChanceExecutionSuccess(new RandomChanceSkill(player, subSkillType, hasCap, resultModifier)); return checkRandomChanceExecutionSuccess(new RandomChanceSkill(player, subSkillType, hasCap, resultModifier));
} }
public static boolean checkRandomChanceExecutionSuccess(Player player, SubSkillType subSkillType, double resultModifier) public static boolean checkRandomChanceExecutionSuccess(@Nullable Player player, @NotNull SubSkillType subSkillType, double resultModifier) {
{
return checkRandomChanceExecutionSuccess(new RandomChanceSkill(player, subSkillType, resultModifier)); return checkRandomChanceExecutionSuccess(new RandomChanceSkill(player, subSkillType, resultModifier));
} }
public static boolean checkRandomStaticChanceExecutionSuccess(Player player, SubSkillType subSkillType, double resultModifier) public static boolean checkRandomStaticChanceExecutionSuccess(@Nullable Player player, @NotNull SubSkillType subSkillType) {
{
try { try {
return checkRandomChanceExecutionSuccess(new RandomChanceSkillStatic(getStaticRandomChance(subSkillType), player, subSkillType)); return checkRandomChanceExecutionSuccess(new RandomChanceSkillStatic(getStaticRandomChance(subSkillType), player, subSkillType));
} catch (InvalidStaticChance invalidStaticChance) { } catch (InvalidStaticChance invalidStaticChance) {
@ -273,21 +233,15 @@ public class RandomChanceUtil
return false; return false;
} }
public static boolean checkRandomStaticChanceExecutionSuccess(Player player, SubSkillType subSkillType)
{
return checkRandomStaticChanceExecutionSuccess(player, subSkillType, 1.0F);
}
/** /**
* Grabs static activation rolls for Secondary Abilities * Grabs static activation rolls for Secondary Abilities
*
* @param subSkillType The secondary ability to grab properties of * @param subSkillType The secondary ability to grab properties of
* @throws InvalidStaticChance if the skill has no defined static chance this exception will be thrown and you should know you're a naughty boy
* @return The static activation roll involved in the RNG calculation * @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(SubSkillType subSkillType) throws InvalidStaticChance public static double getStaticRandomChance(@NotNull SubSkillType subSkillType) throws InvalidStaticChance {
{ switch (subSkillType) {
switch(subSkillType)
{
case AXES_ARMOR_IMPACT: case AXES_ARMOR_IMPACT:
return AdvancedConfig.getInstance().getImpactChance(); return AdvancedConfig.getInstance().getImpactChance();
case AXES_GREATER_IMPACT: case AXES_GREATER_IMPACT:
@ -299,24 +253,12 @@ public class RandomChanceUtil
} }
} }
public static boolean sendSkillEvent(Player player, SubSkillType subSkillType, double activationChance) public static boolean sendSkillEvent(Player player, SubSkillType subSkillType, double activationChance) {
{
SubSkillRandomCheckEvent event = new SubSkillRandomCheckEvent(player, subSkillType, activationChance); SubSkillRandomCheckEvent event = new SubSkillRandomCheckEvent(player, subSkillType, activationChance);
return !event.isCancelled(); return !event.isCancelled();
} }
/*public static boolean treasureDropSuccessful(Player player, double dropChance, int activationChance) { public static String @NotNull [] calculateAbilityDisplayValues(@NotNull SkillActivationType skillActivationType, @NotNull Player player, @NotNull SubSkillType subSkillType) {
SubSkillRandomCheckEvent event = new SubSkillRandomCheckEvent(player, SubSkillType.EXCAVATION_ARCHAEOLOGY, dropChance / activationChance);
mcMMO.p.getServer().getPluginManager().callEvent(event);
return (event.getChance() * activationChance) > (Misc.getRandom().nextDouble() * activationChance) && !event.isCancelled();
}*/
public static boolean isActivationSuccessful(SkillActivationType skillActivationType, AbstractSubSkill abstractSubSkill, Player player)
{
return isActivationSuccessful(skillActivationType, abstractSubSkill.getSubSkillType(), player);
}
public static String[] calculateAbilityDisplayValues(@NotNull SkillActivationType skillActivationType, @NotNull Player player, @NotNull SubSkillType subSkillType) {
double successChance = getActivationChance(skillActivationType, subSkillType, player); double successChance = getActivationChance(skillActivationType, subSkillType, player);
String[] displayValues = new String[2]; String[] displayValues = new String[2];
@ -328,7 +270,7 @@ public class RandomChanceUtil
return displayValues; return displayValues;
} }
public static String[] calculateAbilityDisplayValuesStatic(@NotNull OnlineMMOPlayer mmoPlayer, PrimarySkillType primarySkillType, double chance) { public static String @NotNull [] calculateAbilityDisplayValuesStatic(@NotNull Player player, @NotNull PrimarySkillType primarySkillType, double chance) {
RandomChanceStatic rcs = new RandomChanceStatic(chance, false); RandomChanceStatic rcs = new RandomChanceStatic(chance, false);
double successChance = getRandomChanceExecutionChance(rcs); double successChance = getRandomChanceExecutionChance(rcs);
@ -337,7 +279,7 @@ public class RandomChanceUtil
String[] displayValues = new String[2]; String[] displayValues = new String[2];
boolean isLucky = Permissions.lucky(Misc.adaptPlayer(mmoPlayer), primarySkillType); boolean isLucky = Permissions.lucky(player, primarySkillType);
displayValues[0] = percent.format(Math.min(successChance, 100.0D) / 100.0D); 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; displayValues[1] = isLucky ? percent.format(Math.min(successChance_lucky, 100.0D) / 100.0D) : null;
@ -345,7 +287,7 @@ public class RandomChanceUtil
return displayValues; return displayValues;
} }
public static String[] calculateAbilityDisplayValuesCustom(SkillActivationType skillActivationType, Player player, SubSkillType subSkillType, double multiplier) { public static String @NotNull [] calculateAbilityDisplayValuesCustom(@NotNull SkillActivationType skillActivationType, @NotNull Player player, @NotNull SubSkillType subSkillType, double multiplier) {
double successChance = getActivationChance(skillActivationType, subSkillType, player); double successChance = getActivationChance(skillActivationType, subSkillType, player);
successChance *= multiplier; //Currently only used for graceful roll successChance *= multiplier; //Currently only used for graceful roll
String[] displayValues = new String[2]; String[] displayValues = new String[2];
@ -360,19 +302,25 @@ public class RandomChanceUtil
return displayValues; return displayValues;
} }
public static double addLuck(Player player, PrimarySkillType primarySkillType, double chance) public static double addLuck(@NotNull Player player, @NotNull PrimarySkillType primarySkillType, double chance) {
{
if (Permissions.lucky(player, primarySkillType)) if (Permissions.lucky(player, primarySkillType))
return chance * 1.333D; return chance * LUCKY_MODIFIER;
else else
return chance; return chance;
} }
public static double addLuck(boolean isLucky, double chance) public static double addLuck(boolean isLucky, double chance) {
{
if (isLucky) if (isLucky)
return chance * 1.333D; return chance * LUCKY_MODIFIER;
else else
return chance; 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);
}
} }

View File

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

View File

@ -6,7 +6,6 @@ import com.gmail.nossr50.events.skills.SkillActivationPerkEvent;
import com.gmail.nossr50.mcMMO; import com.gmail.nossr50.mcMMO;
import com.gmail.nossr50.util.Permissions; import com.gmail.nossr50.util.Permissions;
import com.gmail.nossr50.util.player.UserManager; import com.gmail.nossr50.util.player.UserManager;
import org.bukkit.Bukkit; import org.bukkit.Bukkit;
import org.bukkit.ChatColor; import org.bukkit.ChatColor;
import org.bukkit.entity.Player; import org.bukkit.entity.Player;

View File

@ -0,0 +1,17 @@
package com.gmail.nossr50;
import org.jetbrains.annotations.NotNull;
import java.io.File;
//TODO: Move generic test stuff here
public class TestUtil {
public static void recursiveDelete(@NotNull File directoryToBeDeleted) {
if (directoryToBeDeleted.isDirectory()) {
for (File file : directoryToBeDeleted.listFiles()) {
recursiveDelete(file);
}
}
directoryToBeDeleted.delete();
}
}

View File

@ -0,0 +1,37 @@
package com.gmail.nossr50.datatypes;
import org.bukkit.Location;
import org.junit.Assert;
import org.junit.Test;
public class BlockLocationHistoryTest {
@Test
public void testRemovesOldestElement() {
BlockLocationHistory history = new BlockLocationHistory(2);
Location locationA = new Location(null, 0, 1, 2);
Location locationB = new Location(null, 1, 2, 3);
Location locationC = new Location(null, 2, 3, 4);
history.add(locationA);
history.add(locationB);
history.add(locationC);
Assert.assertFalse(history.contains(locationA));
Assert.assertTrue(history.contains(locationB));
Assert.assertTrue(history.contains(locationC));
}
@Test
public void testSupportsDuplicateElement() {
BlockLocationHistory history = new BlockLocationHistory(2);
Location locationA = new Location(null, 0, 1, 2);
Location locationB = new Location(null, 1, 2, 3);
history.add(locationA);
history.add(locationA);
history.add(locationB);
Assert.assertTrue(history.contains(locationA));
Assert.assertTrue(history.contains(locationB));
history.add(locationB);
Assert.assertFalse(history.contains(locationA));
}
}

View File

@ -1,10 +1,11 @@
import com.gmail.nossr50.util.blockmeta.*; package com.gmail.nossr50.util.blockmeta;
import com.gmail.nossr50.TestUtil;
import com.google.common.io.Files; import com.google.common.io.Files;
import org.bukkit.Bukkit; import org.bukkit.Bukkit;
import org.bukkit.World; import org.bukkit.World;
import org.bukkit.block.Block; import org.bukkit.block.Block;
import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.junit.*; import org.junit.*;
import org.junit.runner.RunWith; import org.junit.runner.RunWith;
import org.mockito.Mockito; import org.mockito.Mockito;
@ -31,7 +32,7 @@ public class ChunkStoreTest {
@AfterClass @AfterClass
public static void tearDownClass() { public static void tearDownClass() {
recursiveDelete(tempDir); TestUtil.recursiveDelete(tempDir);
} }
private World mockWorld; private World mockWorld;
@ -184,15 +185,6 @@ public class ChunkStoreTest {
Assert.assertTrue(expected.isTrue(x, y, z) == actual.isTrue(x, y, z)); Assert.assertTrue(expected.isTrue(x, y, z) == actual.isTrue(x, y, z));
} }
private static void recursiveDelete(@NotNull File directoryToBeDeleted) {
if (directoryToBeDeleted.isDirectory()) {
for (File file : directoryToBeDeleted.listFiles()) {
recursiveDelete(file);
}
}
directoryToBeDeleted.delete();
}
private static byte[] serializeChunkstore(@NotNull ChunkStore chunkStore) throws IOException { private static byte[] serializeChunkstore(@NotNull ChunkStore chunkStore) throws IOException {
ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream(); ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
if (chunkStore instanceof BitSetChunkStore) if (chunkStore instanceof BitSetChunkStore)

View File

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