mirror of
https://github.com/mcMMO-Dev/mcMMO.git
synced 2025-06-28 03:34:43 +02:00
mcMMO 2.2.000-RC1 candidate for release
This commit is contained in:
@ -7,8 +7,7 @@ import com.gmail.nossr50.datatypes.skills.SubSkillType;
|
||||
import com.gmail.nossr50.mcMMO;
|
||||
import com.gmail.nossr50.skills.repair.Repair;
|
||||
import com.gmail.nossr50.skills.salvage.Salvage;
|
||||
import com.gmail.nossr50.util.random.RandomChanceSkill;
|
||||
import com.gmail.nossr50.util.random.RandomChanceUtil;
|
||||
import com.gmail.nossr50.util.random.ProbabilityUtil;
|
||||
import org.bukkit.Material;
|
||||
import org.bukkit.World;
|
||||
import org.bukkit.block.Block;
|
||||
@ -98,7 +97,7 @@ public final class BlockUtils {
|
||||
*/
|
||||
public static boolean checkDoubleDrops(Player player, BlockState blockState, PrimarySkillType skillType, SubSkillType subSkillType) {
|
||||
if (mcMMO.p.getGeneralConfig().getDoubleDropsEnabled(skillType, blockState.getType()) && Permissions.isSubSkillEnabled(player, subSkillType)) {
|
||||
return RandomChanceUtil.checkRandomChanceExecutionSuccess(new RandomChanceSkill(player, subSkillType, true));
|
||||
return ProbabilityUtil.isSkillRNGSuccessful(subSkillType, player);
|
||||
}
|
||||
|
||||
return false;
|
||||
@ -231,22 +230,22 @@ public final class BlockUtils {
|
||||
return mcMMO.getMaterialMapStore().isTreeFellerDestructible(material);
|
||||
}
|
||||
|
||||
/**
|
||||
* Determine if a given block should be affected by Flux Mining
|
||||
*
|
||||
* @param blockState The {@link BlockState} of the block to check
|
||||
* @return true if the block should affected by Flux Mining, false otherwise
|
||||
*/
|
||||
public static boolean affectedByFluxMining(BlockState blockState) {
|
||||
switch (blockState.getType()) {
|
||||
case IRON_ORE:
|
||||
case GOLD_ORE:
|
||||
return true;
|
||||
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
// /**
|
||||
// * Determine if a given block should be affected by Flux Mining
|
||||
// *
|
||||
// * @param blockState The {@link BlockState} of the block to check
|
||||
// * @return true if the block should affected by Flux Mining, false otherwise
|
||||
// */
|
||||
// public static boolean affectedByFluxMining(BlockState blockState) {
|
||||
// switch (blockState.getType()) {
|
||||
// case IRON_ORE:
|
||||
// case GOLD_ORE:
|
||||
// return true;
|
||||
//
|
||||
// default:
|
||||
// return false;
|
||||
// }
|
||||
// }
|
||||
|
||||
/**
|
||||
* Determine if a given block can activate Herbalism abilities
|
||||
|
@ -183,8 +183,7 @@ public final class EventUtils {
|
||||
* @param subSkillType target subskill
|
||||
* @return the event after it has been fired
|
||||
*/
|
||||
@Deprecated
|
||||
public static @NotNull SubSkillEvent callSubSkillEvent(Player player, SubSkillType subSkillType) {
|
||||
public static @NotNull SubSkillEvent callSubSkillEvent(@NotNull Player player, @NotNull SubSkillType subSkillType) {
|
||||
SubSkillEvent event = new SubSkillEvent(player, subSkillType);
|
||||
mcMMO.p.getServer().getPluginManager().callEvent(event);
|
||||
|
||||
@ -399,7 +398,7 @@ public final class EventUtils {
|
||||
McMMOPlayer mmoPlayer = UserManager.getPlayer(player);
|
||||
if(mmoPlayer == null)
|
||||
return true;
|
||||
|
||||
|
||||
McMMOPlayerXpGainEvent event = new McMMOPlayerXpGainEvent(player, skill, xpGained, xpGainReason);
|
||||
mcMMO.p.getServer().getPluginManager().callEvent(event);
|
||||
|
||||
|
@ -36,14 +36,26 @@ public final class ItemUtils {
|
||||
* @param item Item to check
|
||||
* @return true if the item is a bow, false otherwise
|
||||
*/
|
||||
// TODO: Unit tests
|
||||
public static boolean isBow(@NotNull ItemStack item) {
|
||||
return mcMMO.getMaterialMapStore().isBow(item.getType().getKey().getKey());
|
||||
}
|
||||
|
||||
// TODO: Unit tests
|
||||
public static boolean isCrossbow(@NotNull ItemStack item) {
|
||||
return mcMMO.getMaterialMapStore().isCrossbow(item.getType().getKey().getKey());
|
||||
}
|
||||
|
||||
// TODO: Unit tests
|
||||
public static boolean isBowOrCrossbow(@NotNull ItemStack item) {
|
||||
return isBow(item) || isCrossbow(item);
|
||||
}
|
||||
|
||||
// TODO: Unit tests
|
||||
public static boolean isTrident(@NotNull ItemStack item) {
|
||||
return mcMMO.getMaterialMapStore().isTrident(item.getType().getKey().getKey());
|
||||
}
|
||||
|
||||
public static boolean hasItemInEitherHand(@NotNull Player player, Material material) {
|
||||
return player.getInventory().getItemInMainHand().getType() == material || player.getInventory().getItemInOffHand().getType() == material;
|
||||
}
|
||||
|
@ -10,9 +10,6 @@ import java.util.Locale;
|
||||
/**
|
||||
* Stores hash tables for item and block names
|
||||
* This allows for better support across multiple versions of Minecraft
|
||||
*
|
||||
* This is a temporary class, mcMMO is spaghetti and I'l clean it up later
|
||||
*
|
||||
*/
|
||||
public class MaterialMapStore {
|
||||
|
||||
@ -52,7 +49,6 @@ public class MaterialMapStore {
|
||||
private final @NotNull HashSet<String> bows;
|
||||
private final @NotNull HashSet<String> crossbows;
|
||||
private final @NotNull HashSet<String> tools;
|
||||
|
||||
private final @NotNull HashSet<String> enchantables;
|
||||
|
||||
private final @NotNull HashSet<String> ores;
|
||||
@ -820,6 +816,14 @@ public class MaterialMapStore {
|
||||
return crossbows.contains(id);
|
||||
}
|
||||
|
||||
public boolean isTrident(@NotNull Material material) {
|
||||
return isTrident(material.getKey().getKey());
|
||||
}
|
||||
|
||||
public boolean isTrident(@NotNull String id) {
|
||||
return tridents.contains(id);
|
||||
}
|
||||
|
||||
public boolean isLeatherArmor(@NotNull Material material) {
|
||||
return isLeatherArmor(material.getKey().getKey());
|
||||
}
|
||||
|
@ -14,6 +14,9 @@ public class MetadataConstants {
|
||||
* Take great care if you ever modify the value of these keys
|
||||
*/
|
||||
public static final @NotNull String METADATA_KEY_REPLANT = "mcMMO: Recently Replanted";
|
||||
public static final @NotNull String METADATA_KEY_SPAWNED_ARROW = "mcMMO: Spawned Arrow";
|
||||
public static final @NotNull String METADATA_KEY_MULTI_SHOT_ARROW = "mcMMO: Multi-shot Arrow";
|
||||
public static final @NotNull String METADATA_KEY_BOUNCE_COUNT = "mcMMO: Arrow Bounce Count";
|
||||
public static final @NotNull String METADATA_KEY_EXPLOSION_FROM_RUPTURE = "mcMMO: Rupture Explosion";
|
||||
public static final @NotNull String METADATA_KEY_FISH_HOOK_REF = "mcMMO: Fish Hook Tracker";
|
||||
public static final @NotNull String METADATA_KEY_DODGE_TRACKER = "mcMMO: Dodge Tracker";
|
||||
|
@ -343,4 +343,26 @@ public final class Misc {
|
||||
experienceOrb.setExperience(orbExpValue);
|
||||
}
|
||||
}
|
||||
|
||||
// public static void hackyUnitTest(@NotNull McMMOPlayer normalPlayer) {
|
||||
// mcMMO.p.getLogger().info("Starting hacky unit test...");
|
||||
// int iterations = 1000000;
|
||||
// double ratioDivisor = 10000; //10000 because we run the test 1,000,000 times
|
||||
// double expectedFailRate = 100.0D - RandomChanceUtil.getRandomChanceExecutionSuccess(normalPlayer.getPlayer(), SubSkillType.MINING_MOTHER_LODE, true);
|
||||
//
|
||||
// double win = 0, loss = 0;
|
||||
// for(int x = 0; x < iterations; x++) {
|
||||
// if(RandomChanceUtil.checkRandomChanceExecutionSuccess(normalPlayer.getPlayer(), SubSkillType.MINING_MOTHER_LODE, true)) {
|
||||
// win++;
|
||||
// } else {
|
||||
// loss++;
|
||||
// }
|
||||
// }
|
||||
//
|
||||
// double lossRatio = (loss / ratioDivisor);
|
||||
// mcMMO.p.getLogger().info("Expected Fail Rate: "+expectedFailRate);
|
||||
// mcMMO.p.getLogger().info("Loss Ratio for hacky test: "+lossRatio);
|
||||
//// Assert.assertEquals(lossRatio, expectedFailRate, 0.01D);
|
||||
// }
|
||||
|
||||
}
|
||||
|
@ -26,7 +26,7 @@ public final class MobHealthbarUtils {
|
||||
EntityDamageEvent lastDamageCause = player.getLastDamageCause();
|
||||
String replaceString = lastDamageCause instanceof EntityDamageByEntityEvent ? StringUtils.getPrettyEntityTypeString(((EntityDamageByEntityEvent) lastDamageCause).getDamager().getType()) : "a mob";
|
||||
|
||||
return deathMessage.replaceAll("(?:(\u00A7(?:[0-9A-FK-ORa-fk-or]))*(?:[\u2764\u25A0]{1,10})){1,2}", replaceString);
|
||||
return deathMessage.replaceAll("(?:(§(?:[0-9A-FK-ORa-fk-or]))*(?:[❤■]{1,10})){1,2}", replaceString);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -5,16 +5,18 @@ import com.gmail.nossr50.datatypes.skills.ItemType;
|
||||
import com.gmail.nossr50.datatypes.skills.MaterialType;
|
||||
import com.gmail.nossr50.datatypes.skills.PrimarySkillType;
|
||||
import com.gmail.nossr50.datatypes.skills.SubSkillType;
|
||||
import com.gmail.nossr50.datatypes.skills.subskills.AbstractSubSkill;
|
||||
import com.gmail.nossr50.mcMMO;
|
||||
import com.gmail.nossr50.util.skills.RankUtils;
|
||||
import org.bukkit.Material;
|
||||
import org.bukkit.Server;
|
||||
import org.bukkit.World;
|
||||
import org.bukkit.entity.EntityType;
|
||||
import org.bukkit.entity.Player;
|
||||
import org.bukkit.permissions.Permissible;
|
||||
import org.bukkit.permissions.Permission;
|
||||
import org.bukkit.permissions.PermissionDefault;
|
||||
import org.bukkit.plugin.PluginManager;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
import java.util.Locale;
|
||||
|
||||
@ -164,10 +166,14 @@ public final class Permissions {
|
||||
* SKILLS
|
||||
*/
|
||||
|
||||
public static boolean skillEnabled(Permissible permissible, PrimarySkillType skill) {return permissible.hasPermission("mcmmo.skills." + skill.toString().toLowerCase(Locale.ENGLISH)); }
|
||||
public static boolean skillEnabled(Permissible permissible, PrimarySkillType skill) {
|
||||
return permissible.hasPermission("mcmmo.skills." + skill.toString().toLowerCase(Locale.ENGLISH));
|
||||
}
|
||||
|
||||
public static boolean vanillaXpBoost(Permissible permissible, PrimarySkillType skill) { return permissible.hasPermission("mcmmo.ability." + skill.toString().toLowerCase(Locale.ENGLISH) + ".vanillaxpboost"); }
|
||||
public static boolean isSubSkillEnabled(Permissible permissible, SubSkillType subSkillType) { return permissible.hasPermission(subSkillType.getPermissionNodeAddress()); }
|
||||
public static boolean isSubSkillEnabled(Permissible permissible, AbstractSubSkill abstractSubSkill) { return permissible.hasPermission(abstractSubSkill.getPermissionNode()); }
|
||||
public static boolean isSubSkillEnabled(Permissible permissible, SubSkillType subSkillType) {
|
||||
return permissible.hasPermission(subSkillType.getPermissionNodeAddress());
|
||||
}
|
||||
|
||||
/* ACROBATICS */
|
||||
public static boolean dodge(Permissible permissible) { return permissible.hasPermission("mcmmo.ability.acrobatics.dodge"); }
|
||||
@ -179,6 +185,7 @@ public final class Permissions {
|
||||
public static boolean concoctions(Permissible permissible) { return permissible.hasPermission("mcmmo.ability.alchemy.concoctions"); }
|
||||
|
||||
/* ARCHERY */
|
||||
public static boolean explosiveShot(Permissible permissible) { return permissible.hasPermission("mcmmo.ability.archery.explosiveshot"); }
|
||||
public static boolean arrowRetrieval(Permissible permissible) { return permissible.hasPermission("mcmmo.ability.archery.trackarrows"); }
|
||||
public static boolean daze(Permissible permissible) { return permissible.hasPermission("mcmmo.ability.archery.daze"); }
|
||||
|
||||
@ -225,6 +232,20 @@ public final class Permissions {
|
||||
/* WOODCUTTING */
|
||||
public static boolean treeFeller(Permissible permissible) { return permissible.hasPermission("mcmmo.ability.woodcutting.treefeller"); }
|
||||
|
||||
/* CROSSBOWS */
|
||||
public static boolean superShotgun(Permissible permissible) {
|
||||
return permissible.hasPermission("mcmmo.ability.crossbows.supershotgun");
|
||||
}
|
||||
public static boolean trickShot(Permissible permissible) { return permissible.hasPermission("mcmmo.ability.crossbows.trickshot"); }
|
||||
public static boolean poweredShot(Permissible permissible) { return permissible.hasPermission("mcmmo.ability.crossbows.poweredshot"); }
|
||||
|
||||
/* TRIDENTS */
|
||||
public static boolean tridentsSuper(Permissible permissible) {
|
||||
return false;
|
||||
// return permissible.hasPermission("mcmmo.ability.tridents.superability");
|
||||
}
|
||||
public static boolean tridentsLimitBreak(Permissible permissible) { return permissible.hasPermission("mcmmo.ability.tridents.superability"); }
|
||||
|
||||
/*
|
||||
* PARTY
|
||||
*/
|
||||
@ -256,4 +277,15 @@ public final class Permissions {
|
||||
permission.setDefault(permissionDefault);
|
||||
pluginManager.addPermission(permission);
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if a player can use a skill
|
||||
*
|
||||
* @param player target player
|
||||
* @param subSkillType target subskill
|
||||
* @return true if the player has permission and has the skill unlocked
|
||||
*/
|
||||
public static boolean canUseSubSkill(@NotNull Player player, @NotNull SubSkillType subSkillType) {
|
||||
return isSubSkillEnabled(player, subSkillType) && RankUtils.hasUnlockedSubskill(player, subSkillType);
|
||||
}
|
||||
}
|
||||
|
@ -61,6 +61,9 @@ public final class CommandRegistrationManager {
|
||||
case AXES:
|
||||
command.setExecutor(new AxesCommand());
|
||||
break;
|
||||
case CROSSBOWS:
|
||||
command.setExecutor(new CrossbowsCommand());
|
||||
break;
|
||||
|
||||
case EXCAVATION:
|
||||
command.setExecutor(new ExcavationCommand());
|
||||
@ -97,6 +100,9 @@ public final class CommandRegistrationManager {
|
||||
case TAMING:
|
||||
command.setExecutor(new TamingCommand());
|
||||
break;
|
||||
case TRIDENTS:
|
||||
command.setExecutor(new TridentsCommand());
|
||||
break;
|
||||
|
||||
case UNARMED:
|
||||
command.setExecutor(new UnarmedCommand());
|
||||
|
@ -1,5 +0,0 @@
|
||||
package com.gmail.nossr50.util.random;
|
||||
|
||||
public class InvalidActivationException extends Exception {
|
||||
//Weee
|
||||
}
|
65
src/main/java/com/gmail/nossr50/util/random/Probability.java
Normal file
65
src/main/java/com/gmail/nossr50/util/random/Probability.java
Normal file
@ -0,0 +1,65 @@
|
||||
package com.gmail.nossr50.util.random;
|
||||
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
import java.util.concurrent.ThreadLocalRandom;
|
||||
|
||||
public interface Probability {
|
||||
/**
|
||||
* The value of this Probability
|
||||
* Should return a result between 0 and 1 (inclusive)
|
||||
* A value of 1 or greater represents something that will always succeed
|
||||
* A value of around 0.5 represents something that succeeds around half the time
|
||||
* A value of 0 represents something that will always fail
|
||||
*
|
||||
* @return the value of probability
|
||||
*/
|
||||
double getValue();
|
||||
|
||||
/**
|
||||
* Create a new Probability with the given value
|
||||
* A value of 100 would represent 100% chance of success
|
||||
* A value of 50 would represent 50% chance of success
|
||||
* A value of 0 would represent 0% chance of success
|
||||
* A value of 1 would represent 1% chance of success
|
||||
* A value of 0.5 would represent 0.5% chance of success
|
||||
* A value of 0.01 would represent 0.01% chance of success
|
||||
*
|
||||
* @param percentage the value of the probability
|
||||
* @return a new Probability with the given value
|
||||
*/
|
||||
static @NotNull Probability ofPercent(double percentage) {
|
||||
return new ProbabilityImpl(percentage);
|
||||
}
|
||||
|
||||
/**
|
||||
* Simulates a "roll of the dice"
|
||||
* If the value passed is higher than the "random" value, than it is a successful roll
|
||||
*
|
||||
* @param probabilityValue probability value
|
||||
* @return true for succeeding, false for failing
|
||||
*/
|
||||
static private boolean isSuccessfulRoll(double probabilityValue) {
|
||||
return (probabilityValue) >= ThreadLocalRandom.current().nextDouble(1D);
|
||||
}
|
||||
|
||||
/**
|
||||
* Simulate an outcome on a probability and return true or false for the result of that outcome
|
||||
*
|
||||
* @return true if the probability succeeded, false if it failed
|
||||
*/
|
||||
default boolean evaluate() {
|
||||
return isSuccessfulRoll(getValue());
|
||||
}
|
||||
|
||||
/**
|
||||
* Modify and then Simulate an outcome on a probability and return true or false for the result of that outcome
|
||||
*
|
||||
* @param probabilityMultiplier probability will be multiplied by this before success is checked
|
||||
* @return true if the probability succeeded, false if it failed
|
||||
*/
|
||||
default boolean evaluate(double probabilityMultiplier) {
|
||||
double probabilityValue = getValue() * probabilityMultiplier;
|
||||
return isSuccessfulRoll(probabilityValue);
|
||||
}
|
||||
}
|
@ -0,0 +1,62 @@
|
||||
package com.gmail.nossr50.util.random;
|
||||
|
||||
import com.gmail.nossr50.api.exceptions.ValueOutOfBoundsException;
|
||||
import com.google.common.base.Objects;
|
||||
|
||||
public class ProbabilityImpl implements Probability {
|
||||
|
||||
private final double probabilityValue;
|
||||
|
||||
/**
|
||||
* Create a probability with a static value
|
||||
*
|
||||
* @param percentage the percentage value of the probability
|
||||
*/
|
||||
ProbabilityImpl(double percentage) throws ValueOutOfBoundsException {
|
||||
if (percentage < 0) {
|
||||
throw new ValueOutOfBoundsException("Value should never be negative for Probability! This suggests a coding mistake, contact the devs!");
|
||||
}
|
||||
|
||||
// Convert to a 0-1 floating point representation
|
||||
probabilityValue = percentage / 100.0D;
|
||||
}
|
||||
|
||||
ProbabilityImpl(double xPos, double xCeiling, double probabilityCeiling) throws ValueOutOfBoundsException {
|
||||
if(probabilityCeiling > 100) {
|
||||
throw new ValueOutOfBoundsException("Probability Ceiling should never be above 100!");
|
||||
} else if (probabilityCeiling < 0) {
|
||||
throw new ValueOutOfBoundsException("Probability Ceiling should never be below 0!");
|
||||
}
|
||||
|
||||
//Get the percent success, this will be from 0-100
|
||||
double probabilityPercent = (probabilityCeiling * (xPos / xCeiling));
|
||||
|
||||
//Convert to a 0-1 floating point representation
|
||||
this.probabilityValue = probabilityPercent / 100.0D;
|
||||
}
|
||||
|
||||
@Override
|
||||
public double getValue() {
|
||||
return probabilityValue;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "ProbabilityImpl{" +
|
||||
"probabilityValue=" + probabilityValue +
|
||||
'}';
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object o) {
|
||||
if (this == o) return true;
|
||||
if (o == null || getClass() != o.getClass()) return false;
|
||||
ProbabilityImpl that = (ProbabilityImpl) o;
|
||||
return Double.compare(that.probabilityValue, probabilityValue) == 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return Objects.hashCode(probabilityValue);
|
||||
}
|
||||
}
|
226
src/main/java/com/gmail/nossr50/util/random/ProbabilityUtil.java
Normal file
226
src/main/java/com/gmail/nossr50/util/random/ProbabilityUtil.java
Normal file
@ -0,0 +1,226 @@
|
||||
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.events.skills.secondaryabilities.SubSkillEvent;
|
||||
import com.gmail.nossr50.mcMMO;
|
||||
import com.gmail.nossr50.util.EventUtils;
|
||||
import com.gmail.nossr50.util.Permissions;
|
||||
import com.gmail.nossr50.util.player.UserManager;
|
||||
import org.bukkit.entity.Player;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import java.text.DecimalFormat;
|
||||
|
||||
public class ProbabilityUtil {
|
||||
public static final @NotNull DecimalFormat percent = new DecimalFormat("##0.00%");
|
||||
public static final double LUCKY_MODIFIER = 1.333D;
|
||||
|
||||
/**
|
||||
* Return a chance of success in "percentage" format, show to the player in UI elements
|
||||
*
|
||||
* @param player target player
|
||||
* @param subSkillType target subskill
|
||||
* @param isLucky whether to apply luck modifiers
|
||||
*
|
||||
* @return "percentage" representation of success
|
||||
*/
|
||||
public static double chanceOfSuccessPercentage(@NotNull Player player,
|
||||
@NotNull SubSkillType subSkillType,
|
||||
boolean isLucky) {
|
||||
Probability probability = getSubSkillProbability(subSkillType, player);
|
||||
//Probability values are on a 0-1 scale and need to be "transformed" into a 1-100 scale
|
||||
double percentageValue = probability.getValue(); //Doesn't need to be scaled
|
||||
|
||||
//Apply lucky modifier
|
||||
if(isLucky) {
|
||||
percentageValue *= LUCKY_MODIFIER;
|
||||
}
|
||||
|
||||
return percentageValue;
|
||||
}
|
||||
|
||||
public static double chanceOfSuccessPercentage(@NotNull Probability probability, boolean isLucky) {
|
||||
//Probability values are on a 0-1 scale and need to be "transformed" into a 1-100 scale
|
||||
double percentageValue = probability.getValue();
|
||||
|
||||
//Apply lucky modifier
|
||||
if(isLucky) {
|
||||
percentageValue *= LUCKY_MODIFIER;
|
||||
}
|
||||
|
||||
return percentageValue;
|
||||
}
|
||||
|
||||
static Probability getStaticRandomChance(@NotNull SubSkillType subSkillType) throws InvalidStaticChance {
|
||||
return switch (subSkillType) {
|
||||
case AXES_ARMOR_IMPACT -> Probability.ofPercent(mcMMO.p.getAdvancedConfig().getImpactChance());
|
||||
case AXES_GREATER_IMPACT -> Probability.ofPercent(mcMMO.p.getAdvancedConfig().getGreaterImpactChance());
|
||||
case TAMING_FAST_FOOD_SERVICE -> Probability.ofPercent(mcMMO.p.getAdvancedConfig().getFastFoodChance());
|
||||
default -> throw new InvalidStaticChance();
|
||||
};
|
||||
}
|
||||
|
||||
static SkillProbabilityType getProbabilityType(@NotNull SubSkillType subSkillType) {
|
||||
SkillProbabilityType skillProbabilityType = SkillProbabilityType.DYNAMIC_CONFIGURABLE;
|
||||
|
||||
if(subSkillType == SubSkillType.TAMING_FAST_FOOD_SERVICE
|
||||
|| subSkillType == SubSkillType.AXES_ARMOR_IMPACT
|
||||
|| subSkillType == SubSkillType.AXES_GREATER_IMPACT)
|
||||
skillProbabilityType = SkillProbabilityType.STATIC_CONFIGURABLE;
|
||||
|
||||
return skillProbabilityType;
|
||||
}
|
||||
|
||||
static @NotNull Probability ofSubSkill(@Nullable Player player,
|
||||
@NotNull SubSkillType subSkillType) {
|
||||
switch (getProbabilityType(subSkillType)) {
|
||||
case DYNAMIC_CONFIGURABLE:
|
||||
double probabilityCeiling;
|
||||
double xCeiling;
|
||||
double xPos;
|
||||
|
||||
if (player != null) {
|
||||
McMMOPlayer mmoPlayer = UserManager.getPlayer(player);
|
||||
if (mmoPlayer == null) {
|
||||
return Probability.ofPercent(0);
|
||||
}
|
||||
xPos = mmoPlayer.getSkillLevel(subSkillType.getParentSkill());
|
||||
} else {
|
||||
xPos = 0;
|
||||
}
|
||||
|
||||
//Probability ceiling is configurable in this type
|
||||
probabilityCeiling = mcMMO.p.getAdvancedConfig().getMaximumProbability(subSkillType);
|
||||
//The xCeiling is configurable in this type
|
||||
xCeiling = mcMMO.p.getAdvancedConfig().getMaxBonusLevel(subSkillType);
|
||||
return new ProbabilityImpl(xPos, xCeiling, probabilityCeiling);
|
||||
case STATIC_CONFIGURABLE:
|
||||
try {
|
||||
return getStaticRandomChance(subSkillType);
|
||||
} catch (InvalidStaticChance invalidStaticChance) {
|
||||
invalidStaticChance.printStackTrace();
|
||||
}
|
||||
default:
|
||||
throw new RuntimeException("No case in switch statement for Skill Probability Type!");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* This is one of several Skill RNG check methods
|
||||
* This helper method is for specific {@link SubSkillType}, which help mcMMO understand where the RNG values used in our calculations come from this {@link SubSkillType}
|
||||
* <p>
|
||||
* 1) Determine where the RNG values come from for the passed {@link SubSkillType}
|
||||
* NOTE: In the config file, there are values which are static and which are more dynamic, this is currently a bit hardcoded and will need to be updated manually
|
||||
* <p>
|
||||
* 2) Determine whether to use Lucky multiplier and influence the outcome
|
||||
* <p>
|
||||
* 3) Creates a {@link Probability} and pipes it to {@link ProbabilityUtil} which processes the result and returns it
|
||||
* <p>
|
||||
* This also calls a {@link SubSkillEvent} which can be cancelled, if it is cancelled this will return false
|
||||
* The outcome of the probability can also be modified by this event that is called
|
||||
*
|
||||
* @param subSkillType target subskill
|
||||
* @param player target player, can be null (null players are given odds equivalent to a player with no levels or luck)
|
||||
* @return true if the Skill RNG succeeds, false if it fails
|
||||
*/
|
||||
public static boolean isSkillRNGSuccessful(@NotNull SubSkillType subSkillType, @NotNull Player player) {
|
||||
//Process probability
|
||||
Probability probability = getSubSkillProbability(subSkillType, player);
|
||||
|
||||
//Send out event
|
||||
SubSkillEvent subSkillEvent = EventUtils.callSubSkillEvent(player, subSkillType);
|
||||
|
||||
if(subSkillEvent.isCancelled()) {
|
||||
return false; //Event got cancelled so this doesn't succeed
|
||||
}
|
||||
|
||||
//Result modifier
|
||||
double resultModifier = subSkillEvent.getResultModifier();
|
||||
|
||||
//Mutate probability
|
||||
if(resultModifier != 1.0D)
|
||||
probability = Probability.ofPercent(probability.getValue() * resultModifier);
|
||||
|
||||
//Luck
|
||||
boolean isLucky = Permissions.lucky(player, subSkillType.getParentSkill());
|
||||
|
||||
if(isLucky) {
|
||||
return probability.evaluate(LUCKY_MODIFIER);
|
||||
} else {
|
||||
return probability.evaluate();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* This is one of several Skill RNG check methods
|
||||
* This helper method is specific to static value RNG, which can be influenced by a player's Luck
|
||||
*
|
||||
* @param primarySkillType the related primary skill
|
||||
* @param player the target player, can be null (null players have the worst odds)
|
||||
* @param probabilityPercentage the probability of this player succeeding in "percentage" format (0-100 inclusive)
|
||||
* @return true if the RNG succeeds, false if it fails
|
||||
*/
|
||||
public static boolean isStaticSkillRNGSuccessful(@NotNull PrimarySkillType primarySkillType, @Nullable Player player, double probabilityPercentage) {
|
||||
//Grab a probability converted from a "percentage" value
|
||||
Probability probability = Probability.ofPercent(probabilityPercentage);
|
||||
|
||||
return isStaticSkillRNGSuccessful(primarySkillType, player, probability);
|
||||
}
|
||||
|
||||
/**
|
||||
* This is one of several Skill RNG check methods
|
||||
* This helper method is specific to static value RNG, which can be influenced by a player's Luck
|
||||
*
|
||||
* @param primarySkillType the related primary skill
|
||||
* @param player the target player, can be null (null players have the worst odds)
|
||||
* @param probability the probability of this player succeeding
|
||||
* @return true if the RNG succeeds, false if it fails
|
||||
*/
|
||||
public static boolean isStaticSkillRNGSuccessful(@NotNull PrimarySkillType primarySkillType, @Nullable Player player, @NotNull Probability probability) {
|
||||
boolean isLucky = player != null && Permissions.lucky(player, primarySkillType);
|
||||
|
||||
if(isLucky) {
|
||||
return probability.evaluate(LUCKY_MODIFIER);
|
||||
} else {
|
||||
return probability.evaluate();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Skills activate without RNG, this allows other plugins to prevent that activation
|
||||
* @param subSkillType target subskill
|
||||
* @param player target player
|
||||
* @return true if the skill succeeds (wasn't cancelled by any other plugin)
|
||||
*/
|
||||
public static boolean isNonRNGSkillActivationSuccessful(@NotNull SubSkillType subSkillType, @NotNull Player player) {
|
||||
return !EventUtils.callSubSkillEvent(player, subSkillType).isCancelled();
|
||||
}
|
||||
|
||||
/**
|
||||
* Grab the {@link Probability} for a specific {@link SubSkillType} for a specific {@link Player}
|
||||
*
|
||||
* @param subSkillType target subskill
|
||||
* @param player target player
|
||||
* @return the Probability of this skill succeeding
|
||||
*/
|
||||
public static @NotNull Probability getSubSkillProbability(@NotNull SubSkillType subSkillType, @Nullable Player player) {
|
||||
return ProbabilityUtil.ofSubSkill(player, subSkillType);
|
||||
}
|
||||
|
||||
public static @NotNull String[] getRNGDisplayValues(@NotNull Player player, @NotNull SubSkillType subSkill) {
|
||||
double firstValue = chanceOfSuccessPercentage(player, subSkill, false);
|
||||
double secondValue = chanceOfSuccessPercentage(player, subSkill, true);
|
||||
|
||||
return new String[]{percent.format(firstValue), percent.format(secondValue)};
|
||||
}
|
||||
|
||||
public static @NotNull String[] getRNGDisplayValues(@NotNull Probability probability) {
|
||||
double firstValue = chanceOfSuccessPercentage(probability, false);
|
||||
double secondValue = chanceOfSuccessPercentage(probability, true);
|
||||
|
||||
return new String[]{percent.format(firstValue), percent.format(secondValue)};
|
||||
}
|
||||
}
|
@ -1,18 +0,0 @@
|
||||
package com.gmail.nossr50.util.random;
|
||||
|
||||
public interface RandomChanceExecution {
|
||||
/**
|
||||
* Gets the XPos used in the formula for success
|
||||
*
|
||||
* @return value of x for our success probability graph
|
||||
*/
|
||||
double getXPos();
|
||||
|
||||
/**
|
||||
* The maximum odds for this RandomChanceExecution
|
||||
* For example, if this value is 10, then 10% odds would be the maximum and would be achieved only when xPos equaled the LinearCurvePeak
|
||||
*
|
||||
* @return maximum probability odds from 0.00 (no chance of ever happened) to 100.0 (probability can be guaranteed)
|
||||
*/
|
||||
double getProbabilityCap();
|
||||
}
|
@ -1,176 +0,0 @@
|
||||
package com.gmail.nossr50.util.random;
|
||||
|
||||
import com.gmail.nossr50.datatypes.player.McMMOPlayer;
|
||||
import com.gmail.nossr50.datatypes.skills.SubSkillType;
|
||||
import com.gmail.nossr50.util.Permissions;
|
||||
import com.gmail.nossr50.util.player.UserManager;
|
||||
import org.bukkit.entity.Player;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
public class RandomChanceSkill implements RandomChanceExecution {
|
||||
protected final double probabilityCap;
|
||||
protected final boolean isLucky;
|
||||
protected int skillLevel;
|
||||
protected final double resultModifier;
|
||||
protected final double maximumBonusLevelCap;
|
||||
|
||||
public RandomChanceSkill(@Nullable Player player, @NotNull SubSkillType subSkillType, double resultModifier) {
|
||||
this.probabilityCap = RandomChanceUtil.LINEAR_CURVE_VAR;
|
||||
|
||||
final McMMOPlayer mcMMOPlayer = UserManager.getPlayer(player);
|
||||
if (player != null && mcMMOPlayer != null) {
|
||||
this.skillLevel = mcMMOPlayer.getSkillLevel(subSkillType.getParentSkill());
|
||||
} else {
|
||||
this.skillLevel = 0;
|
||||
}
|
||||
|
||||
if (player != null)
|
||||
isLucky = Permissions.lucky(player, subSkillType.getParentSkill());
|
||||
else
|
||||
isLucky = false;
|
||||
|
||||
this.resultModifier = resultModifier;
|
||||
this.maximumBonusLevelCap = RandomChanceUtil.getMaxBonusLevelCap(subSkillType);
|
||||
}
|
||||
|
||||
public RandomChanceSkill(@Nullable Player player, @NotNull SubSkillType subSkillType) {
|
||||
this.probabilityCap = RandomChanceUtil.LINEAR_CURVE_VAR;
|
||||
|
||||
final McMMOPlayer mcMMOPlayer = UserManager.getPlayer(player);
|
||||
if (player != null && mcMMOPlayer != null) {
|
||||
this.skillLevel = mcMMOPlayer.getSkillLevel(subSkillType.getParentSkill());
|
||||
} else {
|
||||
this.skillLevel = 0;
|
||||
}
|
||||
|
||||
if (player != null)
|
||||
isLucky = Permissions.lucky(player, subSkillType.getParentSkill());
|
||||
else
|
||||
isLucky = false;
|
||||
|
||||
this.resultModifier = 1.0D;
|
||||
this.maximumBonusLevelCap = RandomChanceUtil.getMaxBonusLevelCap(subSkillType);
|
||||
}
|
||||
|
||||
public RandomChanceSkill(@Nullable Player player, @NotNull SubSkillType subSkillType, boolean hasCap) {
|
||||
if (hasCap)
|
||||
this.probabilityCap = RandomChanceUtil.getMaximumProbability(subSkillType);
|
||||
else
|
||||
this.probabilityCap = RandomChanceUtil.LINEAR_CURVE_VAR;
|
||||
|
||||
final McMMOPlayer mcMMOPlayer = UserManager.getPlayer(player);
|
||||
if (player != null && mcMMOPlayer != null) {
|
||||
this.skillLevel = mcMMOPlayer.getSkillLevel(subSkillType.getParentSkill());
|
||||
} else {
|
||||
this.skillLevel = 0;
|
||||
}
|
||||
|
||||
if (player != null)
|
||||
isLucky = Permissions.lucky(player, subSkillType.getParentSkill());
|
||||
else
|
||||
isLucky = false;
|
||||
|
||||
this.resultModifier = 1.0D;
|
||||
this.maximumBonusLevelCap = RandomChanceUtil.getMaxBonusLevelCap(subSkillType);
|
||||
}
|
||||
|
||||
public RandomChanceSkill(@Nullable Player player, @NotNull SubSkillType subSkillType, boolean hasCap, boolean luckyOverride) {
|
||||
if (hasCap)
|
||||
this.probabilityCap = RandomChanceUtil.getMaximumProbability(subSkillType);
|
||||
else
|
||||
this.probabilityCap = RandomChanceUtil.LINEAR_CURVE_VAR;
|
||||
|
||||
final McMMOPlayer mcMMOPlayer = UserManager.getPlayer(player);
|
||||
if (player != null && mcMMOPlayer != null) {
|
||||
this.skillLevel = mcMMOPlayer.getSkillLevel(subSkillType.getParentSkill());
|
||||
} else {
|
||||
this.skillLevel = 0;
|
||||
}
|
||||
|
||||
isLucky = luckyOverride;
|
||||
|
||||
this.resultModifier = 1.0D;
|
||||
this.maximumBonusLevelCap = RandomChanceUtil.getMaxBonusLevelCap(subSkillType);
|
||||
}
|
||||
|
||||
public RandomChanceSkill(@Nullable Player player, @NotNull SubSkillType subSkillType, boolean hasCap, double resultModifier) {
|
||||
if (hasCap)
|
||||
this.probabilityCap = RandomChanceUtil.getMaximumProbability(subSkillType);
|
||||
else
|
||||
this.probabilityCap = RandomChanceUtil.LINEAR_CURVE_VAR;
|
||||
|
||||
final McMMOPlayer mcMMOPlayer = UserManager.getPlayer(player);
|
||||
if (player != null && mcMMOPlayer != null) {
|
||||
this.skillLevel = mcMMOPlayer.getSkillLevel(subSkillType.getParentSkill());
|
||||
} else {
|
||||
this.skillLevel = 0;
|
||||
}
|
||||
|
||||
if (player != null)
|
||||
isLucky = Permissions.lucky(player, subSkillType.getParentSkill());
|
||||
else
|
||||
isLucky = false;
|
||||
|
||||
this.resultModifier = resultModifier;
|
||||
this.maximumBonusLevelCap = RandomChanceUtil.getMaxBonusLevelCap(subSkillType);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the skill level of the player who owns this RandomChanceSkill
|
||||
*
|
||||
* @return the current skill level relating to this RandomChanceSkill
|
||||
*/
|
||||
public int getSkillLevel() {
|
||||
return skillLevel;
|
||||
}
|
||||
|
||||
/**
|
||||
* Modify the skill level used for this skill's RNG calculations
|
||||
*
|
||||
* @param newSkillLevel new skill level
|
||||
*/
|
||||
public void setSkillLevel(int newSkillLevel) {
|
||||
skillLevel = newSkillLevel;
|
||||
}
|
||||
|
||||
/**
|
||||
* The maximum bonus level for this skill
|
||||
* This is when the skills level no longer increases the odds of success
|
||||
* For example, a value of 25 will mean the success chance no longer grows after skill level 25
|
||||
*
|
||||
* @return the maximum bonus from skill level for this skill
|
||||
*/
|
||||
public double getMaximumBonusLevelCap() {
|
||||
return maximumBonusLevelCap;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the XPos used in the formula for success
|
||||
*
|
||||
* @return value of x for our success probability graph
|
||||
*/
|
||||
@Override
|
||||
public double getXPos() {
|
||||
return getSkillLevel();
|
||||
}
|
||||
|
||||
/**
|
||||
* The maximum odds for this RandomChanceExecution
|
||||
* For example, if this value is 10, then 10% odds would be the maximum and would be achieved only when xPos equaled the LinearCurvePeak
|
||||
*
|
||||
* @return maximum probability odds from 0.00 (no chance of ever happened) to 100.0 (probability can be guaranteed)
|
||||
*/
|
||||
@Override
|
||||
public double getProbabilityCap() {
|
||||
return probabilityCap;
|
||||
}
|
||||
|
||||
public boolean isLucky() {
|
||||
return isLucky;
|
||||
}
|
||||
|
||||
public double getResultModifier() {
|
||||
return resultModifier;
|
||||
}
|
||||
}
|
@ -1,61 +0,0 @@
|
||||
package com.gmail.nossr50.util.random;
|
||||
|
||||
import com.gmail.nossr50.datatypes.skills.SubSkillType;
|
||||
import org.bukkit.entity.Player;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
public class RandomChanceSkillStatic extends RandomChanceSkill {
|
||||
private final double xPos;
|
||||
|
||||
public RandomChanceSkillStatic(double xPos, @Nullable Player player, @NotNull SubSkillType subSkillType) {
|
||||
super(player, subSkillType);
|
||||
|
||||
this.xPos = xPos;
|
||||
}
|
||||
|
||||
public RandomChanceSkillStatic(double xPos, @Nullable Player player, @NotNull SubSkillType subSkillType, boolean luckyOverride) {
|
||||
super(player, subSkillType, false, luckyOverride);
|
||||
|
||||
this.xPos = xPos;
|
||||
}
|
||||
|
||||
public RandomChanceSkillStatic(double xPos, @Nullable Player player, @NotNull SubSkillType subSkillType, double resultModifier) {
|
||||
super(player, subSkillType, resultModifier);
|
||||
|
||||
this.xPos = xPos;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the XPos used in the formula for success
|
||||
*
|
||||
* @return value of x for our success probability graph
|
||||
*/
|
||||
@Override
|
||||
public double getXPos() {
|
||||
return xPos;
|
||||
}
|
||||
|
||||
/**
|
||||
* The maximum odds for this RandomChanceExecution
|
||||
* For example, if this value is 10, then 10% odds would be the maximum and would be achieved only when xPos equaled the LinearCurvePeak
|
||||
*
|
||||
* @return maximum probability odds from 0.00 (no chance of ever happened) to 100.0 (probability can be guaranteed)
|
||||
*/
|
||||
@Override
|
||||
public double getProbabilityCap() {
|
||||
return probabilityCap;
|
||||
}
|
||||
|
||||
/**
|
||||
* The maximum bonus level for this skill
|
||||
* This is when the skills level no longer increases the odds of success
|
||||
* For example, a value of 25 will mean the success chance no longer grows after skill level 25
|
||||
*
|
||||
* @return the maximum bonus from skill level for this skill
|
||||
*/
|
||||
@Override
|
||||
public double getMaximumBonusLevelCap() {
|
||||
return 100;
|
||||
}
|
||||
}
|
@ -1,38 +0,0 @@
|
||||
package com.gmail.nossr50.util.random;
|
||||
|
||||
public class RandomChanceStatic implements RandomChanceExecution {
|
||||
private final double xPos;
|
||||
private final double probabilityCap;
|
||||
private final boolean isLucky;
|
||||
|
||||
public RandomChanceStatic(double xPos, double probabilityCap, boolean isLucky) {
|
||||
this.xPos = xPos;
|
||||
this.probabilityCap = probabilityCap;
|
||||
this.isLucky = isLucky;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the XPos used in the formula for success
|
||||
*
|
||||
* @return value of x for our success probability graph
|
||||
*/
|
||||
@Override
|
||||
public double getXPos() {
|
||||
return xPos;
|
||||
}
|
||||
|
||||
/**
|
||||
* The maximum odds for this RandomChanceExecution
|
||||
* For example, if this value is 10, then 10% odds would be the maximum and would be achieved only when xPos equaled the LinearCurvePeak
|
||||
*
|
||||
* @return maximum probability odds from 0.00 (no chance of ever happened) to 100.0 (probability can be guaranteed)
|
||||
*/
|
||||
@Override
|
||||
public double getProbabilityCap() {
|
||||
return probabilityCap;
|
||||
}
|
||||
|
||||
public boolean isLucky() {
|
||||
return isLucky;
|
||||
}
|
||||
}
|
@ -1,337 +0,0 @@
|
||||
package com.gmail.nossr50.util.random;
|
||||
|
||||
import com.gmail.nossr50.datatypes.skills.PrimarySkillType;
|
||||
import com.gmail.nossr50.datatypes.skills.SubSkillType;
|
||||
import com.gmail.nossr50.events.skills.secondaryabilities.SubSkillEvent;
|
||||
import com.gmail.nossr50.events.skills.secondaryabilities.SubSkillRandomCheckEvent;
|
||||
import com.gmail.nossr50.mcMMO;
|
||||
import com.gmail.nossr50.util.EventUtils;
|
||||
import com.gmail.nossr50.util.Permissions;
|
||||
import com.gmail.nossr50.util.skills.SkillActivationType;
|
||||
import org.bukkit.entity.Player;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import java.text.DecimalFormat;
|
||||
import java.util.concurrent.ThreadLocalRandom;
|
||||
|
||||
public class RandomChanceUtil {
|
||||
public static final @NotNull DecimalFormat percent = new DecimalFormat("##0.00%");
|
||||
//public static final DecimalFormat decimal = new DecimalFormat("##0.00");
|
||||
public static final double LINEAR_CURVE_VAR = 100.0D;
|
||||
public static final double LUCKY_MODIFIER = 1.333D;
|
||||
|
||||
/**
|
||||
* This method is the final step in determining if a Sub-Skill / Secondary Skill in mcMMO successfully activates either from chance or otherwise
|
||||
* Random skills check for success based on numbers and then fire a cancellable event, if that event is not cancelled they succeed
|
||||
* non-RNG skills just fire the cancellable event and succeed if they go uncancelled
|
||||
*
|
||||
* @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(@NotNull SkillActivationType skillActivationType, @NotNull SubSkillType subSkillType, @Nullable Player player) {
|
||||
switch (skillActivationType) {
|
||||
case RANDOM_LINEAR_100_SCALE_WITH_CAP:
|
||||
return checkRandomChanceExecutionSuccess(player, subSkillType, true);
|
||||
case RANDOM_STATIC_CHANCE:
|
||||
return checkRandomStaticChanceExecutionSuccess(player, subSkillType);
|
||||
case ALWAYS_FIRES:
|
||||
SubSkillEvent event = EventUtils.callSubSkillEvent(player, subSkillType);
|
||||
return !event.isCancelled();
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
public static double getActivationChance(@NotNull SkillActivationType skillActivationType, @NotNull SubSkillType subSkillType, @Nullable Player player, boolean luckyOverride) {
|
||||
switch (skillActivationType) {
|
||||
case RANDOM_LINEAR_100_SCALE_WITH_CAP:
|
||||
return getRandomChanceExecutionSuccess(player, subSkillType, true, luckyOverride);
|
||||
case RANDOM_STATIC_CHANCE:
|
||||
return getRandomStaticChanceExecutionSuccess(player, subSkillType, luckyOverride);
|
||||
default:
|
||||
return 0.1337;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks whether or not the random chance succeeds
|
||||
*
|
||||
* @return true if the random chance succeeds
|
||||
*/
|
||||
public static boolean checkRandomChanceExecutionSuccess(@NotNull Player player, @NotNull PrimarySkillType primarySkillType, double chance) {
|
||||
//Check the odds
|
||||
chance *= 100;
|
||||
|
||||
chance = addLuck(player, primarySkillType, chance);
|
||||
|
||||
/*
|
||||
* Stuff like treasures can specify a drop chance from 0.05 to 100
|
||||
* Because of that we need to use a large int bound and multiply the chance by 100
|
||||
*/
|
||||
return rollDice(chance, 10000);
|
||||
}
|
||||
|
||||
public static boolean rollDice(double chanceOfSuccess, int bound) {
|
||||
return rollDice(chanceOfSuccess, bound, 1.0F);
|
||||
}
|
||||
|
||||
public static boolean rollDice(double chanceOfSuccess, int bound, double resultModifier) {
|
||||
return chanceOfSuccess > (ThreadLocalRandom.current().nextInt(bound) * resultModifier);
|
||||
}
|
||||
|
||||
/**
|
||||
* Used for stuff like Excavation, Fishing, etc...
|
||||
*
|
||||
* @param randomChance
|
||||
* @return
|
||||
*/
|
||||
public static boolean checkRandomChanceExecutionSuccess(@NotNull RandomChanceSkillStatic randomChance, double resultModifier) {
|
||||
double chanceOfSuccess = calculateChanceOfSuccess(randomChance);
|
||||
|
||||
//Check the odds
|
||||
return rollDice(chanceOfSuccess, 100, resultModifier);
|
||||
}
|
||||
|
||||
/**
|
||||
* Used for stuff like Excavation, Fishing, etc...
|
||||
*
|
||||
* @param randomChance
|
||||
* @return
|
||||
*/
|
||||
public static boolean checkRandomChanceExecutionSuccess(@NotNull RandomChanceSkillStatic randomChance) {
|
||||
return checkRandomChanceExecutionSuccess(randomChance, 1.0F);
|
||||
}
|
||||
|
||||
public static boolean checkRandomChanceExecutionSuccess(@NotNull RandomChanceSkill randomChance) {
|
||||
double chanceOfSuccess = calculateChanceOfSuccess(randomChance);
|
||||
|
||||
//Check the odds
|
||||
return rollDice(chanceOfSuccess, 100);
|
||||
}
|
||||
|
||||
|
||||
/*public static double getRandomChanceExecutionChance(RandomChanceSkill randomChance)
|
||||
{
|
||||
double chanceOfSuccess = calculateChanceOfSuccess(randomChance);
|
||||
return chanceOfSuccess;
|
||||
}*/
|
||||
|
||||
/**
|
||||
* Gets the Static Chance for something to activate
|
||||
*
|
||||
* @param randomChance
|
||||
* @return
|
||||
*/
|
||||
public static double getRandomChanceExecutionChance(@NotNull RandomChanceExecution randomChance) {
|
||||
return getChanceOfSuccess(randomChance.getXPos(), randomChance.getProbabilityCap(), LINEAR_CURVE_VAR);
|
||||
}
|
||||
|
||||
public static double getRandomChanceExecutionChance(@NotNull RandomChanceExecution randomChance, boolean luckyOverride) {
|
||||
return getChanceOfSuccess(randomChance.getXPos(), randomChance.getProbabilityCap(), LINEAR_CURVE_VAR);
|
||||
}
|
||||
|
||||
public static double getRandomChanceExecutionChance(@NotNull RandomChanceStatic randomChance) {
|
||||
double chanceOfSuccess = getChanceOfSuccess(randomChance.getXPos(), randomChance.getProbabilityCap(), LINEAR_CURVE_VAR);
|
||||
|
||||
chanceOfSuccess = addLuck(randomChance.isLucky(), chanceOfSuccess);
|
||||
|
||||
return chanceOfSuccess;
|
||||
}
|
||||
|
||||
/*private static double calculateChanceOfSuccess(RandomChanceStatic randomChance) {
|
||||
double chanceOfSuccess = getChanceOfSuccess(randomChance.getXPos(), randomChance.getProbabilityCap());
|
||||
return chanceOfSuccess;
|
||||
}*/
|
||||
|
||||
public static double calculateChanceOfSuccess(@NotNull RandomChanceSkill randomChance) {
|
||||
double skillLevel = randomChance.getSkillLevel();
|
||||
double maximumProbability = randomChance.getProbabilityCap();
|
||||
double maximumBonusLevel = randomChance.getMaximumBonusLevelCap();
|
||||
|
||||
double chanceOfSuccess;
|
||||
|
||||
if (skillLevel >= maximumBonusLevel) {
|
||||
//Chance of success is equal to the maximum probability if the maximum bonus level has been reached
|
||||
chanceOfSuccess = maximumProbability;
|
||||
} else {
|
||||
//Get chance of success
|
||||
chanceOfSuccess = getChanceOfSuccess(randomChance.getXPos(), maximumProbability, maximumBonusLevel);
|
||||
}
|
||||
|
||||
//Add Luck
|
||||
chanceOfSuccess = addLuck(randomChance.isLucky(), chanceOfSuccess);
|
||||
|
||||
return chanceOfSuccess;
|
||||
}
|
||||
|
||||
public static double calculateChanceOfSuccess(@NotNull RandomChanceSkillStatic randomChance) {
|
||||
double chanceOfSuccess = getChanceOfSuccess(randomChance.getXPos(), 100, 100);
|
||||
|
||||
//Add Luck
|
||||
chanceOfSuccess = addLuck(randomChance.isLucky(), chanceOfSuccess);
|
||||
|
||||
return chanceOfSuccess;
|
||||
}
|
||||
|
||||
/**
|
||||
* The formula for RNG success is determined like this
|
||||
* maximum probability * ( x / maxlevel )
|
||||
*
|
||||
* @return the chance of success from 0-100 (100 = guaranteed)
|
||||
*/
|
||||
private static int getChanceOfSuccess(double skillLevel, double maxProbability, double maxLevel) {
|
||||
//return (int) (x / (y / LINEAR_CURVE_VAR));
|
||||
return (int) (maxProbability * (skillLevel / maxLevel));
|
||||
// max probability * (weight/maxlevel) = chance of success
|
||||
}
|
||||
|
||||
private static int getChanceOfSuccess(double x, double y) {
|
||||
return (int) (x / (y / LINEAR_CURVE_VAR));
|
||||
// max probability * (weight/maxlevel) = chance of success
|
||||
}
|
||||
|
||||
public static double getRandomChanceExecutionSuccess(@Nullable Player player, @NotNull SubSkillType subSkillType, boolean hasCap) {
|
||||
RandomChanceSkill rcs = new RandomChanceSkill(player, subSkillType, hasCap);
|
||||
return calculateChanceOfSuccess(rcs);
|
||||
}
|
||||
|
||||
public static double getRandomChanceExecutionSuccess(@Nullable Player player, @NotNull SubSkillType subSkillType, boolean hasCap, boolean luckyOverride) {
|
||||
RandomChanceSkill rcs = new RandomChanceSkill(player, subSkillType, hasCap, luckyOverride);
|
||||
return calculateChanceOfSuccess(rcs);
|
||||
}
|
||||
|
||||
public static double getRandomStaticChanceExecutionSuccess(@Nullable Player player, @NotNull SubSkillType subSkillType, boolean luckyOverride) {
|
||||
try {
|
||||
return getRandomChanceExecutionChance(new RandomChanceSkillStatic(getStaticRandomChance(subSkillType), player, subSkillType, luckyOverride));
|
||||
} catch (InvalidStaticChance invalidStaticChance) {
|
||||
//Catch invalid static skills
|
||||
invalidStaticChance.printStackTrace();
|
||||
}
|
||||
|
||||
return 0.1337; //Puts on shades
|
||||
}
|
||||
|
||||
public static boolean checkRandomChanceExecutionSuccess(@Nullable Player player, @NotNull SubSkillType subSkillType, boolean hasCap) {
|
||||
return checkRandomChanceExecutionSuccess(new RandomChanceSkill(player, subSkillType, hasCap));
|
||||
}
|
||||
|
||||
public static boolean checkRandomChanceExecutionSuccess(@Nullable Player player, @NotNull SubSkillType subSkillType) {
|
||||
return checkRandomChanceExecutionSuccess(new RandomChanceSkill(player, subSkillType));
|
||||
}
|
||||
|
||||
public static boolean checkRandomChanceExecutionSuccess(@Nullable Player player, @NotNull SubSkillType subSkillType, boolean hasCap, double resultModifier) {
|
||||
return checkRandomChanceExecutionSuccess(new RandomChanceSkill(player, subSkillType, hasCap, resultModifier));
|
||||
}
|
||||
|
||||
public static boolean checkRandomChanceExecutionSuccess(@Nullable Player player, @NotNull SubSkillType subSkillType, double resultModifier) {
|
||||
return checkRandomChanceExecutionSuccess(new RandomChanceSkill(player, subSkillType, resultModifier));
|
||||
}
|
||||
|
||||
|
||||
public static boolean checkRandomStaticChanceExecutionSuccess(@Nullable Player player, @NotNull SubSkillType subSkillType) {
|
||||
try {
|
||||
return checkRandomChanceExecutionSuccess(new RandomChanceSkillStatic(getStaticRandomChance(subSkillType), player, subSkillType));
|
||||
} catch (InvalidStaticChance invalidStaticChance) {
|
||||
//Catch invalid static skills
|
||||
invalidStaticChance.printStackTrace();
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Grabs static activation rolls for Secondary Abilities
|
||||
*
|
||||
* @param subSkillType The secondary ability to grab properties of
|
||||
* @return The static activation roll involved in the RNG calculation
|
||||
* @throws InvalidStaticChance if the skill has no defined static chance this exception will be thrown and you should know you're a naughty boy
|
||||
*/
|
||||
public static double getStaticRandomChance(@NotNull SubSkillType subSkillType) throws InvalidStaticChance {
|
||||
switch (subSkillType) {
|
||||
case AXES_ARMOR_IMPACT:
|
||||
return mcMMO.p.getAdvancedConfig().getImpactChance();
|
||||
case AXES_GREATER_IMPACT:
|
||||
return mcMMO.p.getAdvancedConfig().getGreaterImpactChance();
|
||||
case TAMING_FAST_FOOD_SERVICE:
|
||||
return mcMMO.p.getAdvancedConfig().getFastFoodChance();
|
||||
default:
|
||||
throw new InvalidStaticChance();
|
||||
}
|
||||
}
|
||||
|
||||
public static boolean sendSkillEvent(Player player, SubSkillType subSkillType, double activationChance) {
|
||||
SubSkillRandomCheckEvent event = new SubSkillRandomCheckEvent(player, subSkillType, activationChance);
|
||||
return !event.isCancelled();
|
||||
}
|
||||
|
||||
public static String @NotNull [] calculateAbilityDisplayValues(@NotNull SkillActivationType skillActivationType, @NotNull Player player, @NotNull SubSkillType subSkillType) {
|
||||
double successChance = getActivationChance(skillActivationType, subSkillType, player, false);
|
||||
double successChanceLucky = getActivationChance(skillActivationType, subSkillType, player, true);
|
||||
|
||||
String[] displayValues = new String[2];
|
||||
|
||||
boolean isLucky = Permissions.lucky(player, subSkillType.getParentSkill());
|
||||
|
||||
displayValues[0] = percent.format(Math.min(successChance, 100.0D) / 100.0D);
|
||||
displayValues[1] = isLucky ? percent.format(Math.min(successChanceLucky, 100.0D) / 100.0D) : null;
|
||||
|
||||
return displayValues;
|
||||
}
|
||||
|
||||
public static String @NotNull [] calculateAbilityDisplayValuesStatic(@NotNull Player player, @NotNull PrimarySkillType primarySkillType, double chance) {
|
||||
RandomChanceStatic rcs = new RandomChanceStatic(chance, LINEAR_CURVE_VAR, false);
|
||||
double successChance = getRandomChanceExecutionChance(rcs);
|
||||
|
||||
RandomChanceStatic rcs_lucky = new RandomChanceStatic(chance, LINEAR_CURVE_VAR, true);
|
||||
double successChance_lucky = getRandomChanceExecutionChance(rcs_lucky);
|
||||
|
||||
String[] displayValues = new String[2];
|
||||
|
||||
boolean isLucky = Permissions.lucky(player, primarySkillType);
|
||||
|
||||
displayValues[0] = percent.format(Math.min(successChance, 100.0D) / 100.0D);
|
||||
displayValues[1] = isLucky ? percent.format(Math.min(successChance_lucky, 100.0D) / 100.0D) : null;
|
||||
|
||||
return displayValues;
|
||||
}
|
||||
|
||||
public static String @NotNull [] calculateAbilityDisplayValuesCustom(@NotNull SkillActivationType skillActivationType, @NotNull Player player, @NotNull SubSkillType subSkillType, double multiplier) {
|
||||
double successChance = getActivationChance(skillActivationType, subSkillType, player, false);
|
||||
double successChanceLucky = getActivationChance(skillActivationType, subSkillType, player, true);
|
||||
//TODO: Most likely incorrectly displays the value for graceful roll but gonna ignore for now...
|
||||
successChance *= multiplier; //Currently only used for graceful roll
|
||||
String[] displayValues = new String[2];
|
||||
|
||||
boolean isLucky = Permissions.lucky(player, subSkillType.getParentSkill());
|
||||
|
||||
displayValues[0] = percent.format(Math.min(successChance, 100.0D) / 100.0D);
|
||||
displayValues[1] = isLucky ? percent.format(Math.min(successChanceLucky, 100.0D) / 100.0D) : null;
|
||||
|
||||
return displayValues;
|
||||
}
|
||||
|
||||
public static double addLuck(@NotNull Player player, @NotNull PrimarySkillType primarySkillType, double chance) {
|
||||
if (Permissions.lucky(player, primarySkillType))
|
||||
return chance * LUCKY_MODIFIER;
|
||||
else
|
||||
return chance;
|
||||
}
|
||||
|
||||
public static double addLuck(boolean isLucky, double chance) {
|
||||
if (isLucky)
|
||||
return chance * LUCKY_MODIFIER;
|
||||
else
|
||||
return chance;
|
||||
}
|
||||
|
||||
public static double getMaximumProbability(@NotNull SubSkillType subSkillType) {
|
||||
return mcMMO.p.getAdvancedConfig().getMaximumProbability(subSkillType);
|
||||
}
|
||||
|
||||
public static double getMaxBonusLevelCap(@NotNull SubSkillType subSkillType) {
|
||||
return mcMMO.p.getAdvancedConfig().getMaxBonusLevel(subSkillType);
|
||||
}
|
||||
}
|
@ -0,0 +1,6 @@
|
||||
package com.gmail.nossr50.util.random;
|
||||
|
||||
public enum SkillProbabilityType {
|
||||
DYNAMIC_CONFIGURABLE, //Has multiple values used for calculation (taken from config files)
|
||||
STATIC_CONFIGURABLE, //A single value used for calculations (taken from config files)
|
||||
}
|
@ -11,7 +11,6 @@ import com.gmail.nossr50.events.scoreboard.ScoreboardEventReason;
|
||||
import com.gmail.nossr50.events.scoreboard.ScoreboardObjectiveEventReason;
|
||||
import com.gmail.nossr50.locale.LocaleLoader;
|
||||
import com.gmail.nossr50.mcMMO;
|
||||
import com.gmail.nossr50.skills.child.FamilyTree;
|
||||
import com.gmail.nossr50.util.LogUtils;
|
||||
import com.gmail.nossr50.util.Misc;
|
||||
import com.gmail.nossr50.util.player.NotificationManager;
|
||||
@ -495,7 +494,7 @@ public class ScoreboardWrapper {
|
||||
sidebarObjective.getScore(ScoreboardManager.LABEL_REMAINING_XP).setScore(mcMMOPlayer.getXpToLevel(targetSkill) - currentXP);
|
||||
}
|
||||
else {
|
||||
for (PrimarySkillType parentSkill : FamilyTree.getParents(targetSkill)) {
|
||||
for (PrimarySkillType parentSkill : mcMMO.p.getSkillTools().getChildSkillParents(targetSkill)) {
|
||||
sidebarObjective.getScore(ScoreboardManager.skillLabels.get(parentSkill)).setScore(mcMMOPlayer.getSkillLevel(parentSkill));
|
||||
}
|
||||
}
|
||||
|
@ -10,13 +10,13 @@ import com.gmail.nossr50.datatypes.skills.SubSkillType;
|
||||
import com.gmail.nossr50.mcMMO;
|
||||
import com.gmail.nossr50.metadata.MobMetaFlagType;
|
||||
import com.gmail.nossr50.metadata.MobMetadataService;
|
||||
import com.gmail.nossr50.party.PartyManager;
|
||||
import com.gmail.nossr50.runnables.skills.AwardCombatXpTask;
|
||||
import com.gmail.nossr50.skills.acrobatics.AcrobaticsManager;
|
||||
import com.gmail.nossr50.skills.archery.ArcheryManager;
|
||||
import com.gmail.nossr50.skills.axes.AxesManager;
|
||||
import com.gmail.nossr50.skills.swords.SwordsManager;
|
||||
import com.gmail.nossr50.skills.taming.TamingManager;
|
||||
import com.gmail.nossr50.skills.tridents.TridentsManager;
|
||||
import com.gmail.nossr50.skills.unarmed.UnarmedManager;
|
||||
import com.gmail.nossr50.util.*;
|
||||
import com.gmail.nossr50.util.player.NotificationManager;
|
||||
@ -45,7 +45,6 @@ public final class CombatUtils {
|
||||
return mcMMO.getMetadataService().getMobMetadataService();
|
||||
}
|
||||
|
||||
//Likely.. because who knows what plugins are throwing around
|
||||
public static boolean isDamageLikelyFromNormalCombat(@NotNull DamageCause damageCause) {
|
||||
return switch (damageCause) {
|
||||
case ENTITY_ATTACK, ENTITY_SWEEP_ATTACK, PROJECTILE -> true;
|
||||
@ -112,6 +111,78 @@ public final class CombatUtils {
|
||||
}
|
||||
}
|
||||
}
|
||||
private static void processTridentCombat(@NotNull LivingEntity target, @NotNull Player player, @NotNull EntityDamageByEntityEvent event) {
|
||||
if (event.getCause() == DamageCause.THORNS) {
|
||||
return;
|
||||
}
|
||||
|
||||
double boostedDamage = event.getDamage();
|
||||
|
||||
McMMOPlayer mcMMOPlayer = UserManager.getPlayer(player);
|
||||
|
||||
//Make sure the profiles been loaded
|
||||
if(mcMMOPlayer == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
TridentsManager tridentsManager = mcMMOPlayer.getTridentsManager();
|
||||
|
||||
if (tridentsManager.canActivateAbility()) {
|
||||
mcMMOPlayer.checkAbilityActivation(PrimarySkillType.TRIDENTS);
|
||||
}
|
||||
|
||||
if (SkillUtils.canUseSubskill(player, SubSkillType.TRIDENTS_IMPALE)) {
|
||||
boostedDamage += (tridentsManager.impaleDamageBonus() * mcMMOPlayer.getAttackStrength());
|
||||
}
|
||||
|
||||
if(canUseLimitBreak(player, target, SubSkillType.TRIDENTS_TRIDENTS_LIMIT_BREAK)) {
|
||||
boostedDamage += (getLimitBreakDamage(player, target, SubSkillType.TRIDENTS_TRIDENTS_LIMIT_BREAK) * mcMMOPlayer.getAttackStrength());
|
||||
}
|
||||
|
||||
event.setDamage(boostedDamage);
|
||||
processCombatXP(mcMMOPlayer, target, PrimarySkillType.TRIDENTS);
|
||||
|
||||
printFinalDamageDebug(player, event, mcMMOPlayer);
|
||||
}
|
||||
|
||||
private static void processCrossbowsCombat(@NotNull LivingEntity target, @NotNull Player player,
|
||||
@NotNull EntityDamageByEntityEvent event, @NotNull Arrow arrow) {
|
||||
double initialDamage = event.getDamage();
|
||||
|
||||
McMMOPlayer mcMMOPlayer = UserManager.getPlayer(player);
|
||||
|
||||
//Make sure the profiles been loaded
|
||||
if(mcMMOPlayer == null) {
|
||||
delayArrowMetaCleanup(arrow);
|
||||
return;
|
||||
}
|
||||
|
||||
double boostedDamage = event.getDamage();
|
||||
|
||||
if (SkillUtils.canUseSubskill(player, SubSkillType.CROSSBOWS_POWERED_SHOT)) {
|
||||
//Not Additive
|
||||
boostedDamage = mcMMOPlayer.getCrossbowsManager().poweredShot(initialDamage);
|
||||
}
|
||||
|
||||
if(canUseLimitBreak(player, target, SubSkillType.CROSSBOWS_CROSSBOWS_LIMIT_BREAK)) {
|
||||
boostedDamage+=getLimitBreakDamage(player, target, SubSkillType.CROSSBOWS_CROSSBOWS_LIMIT_BREAK);
|
||||
}
|
||||
|
||||
double distanceMultiplier = ArcheryManager.distanceXpBonusMultiplier(target, arrow);
|
||||
double forceMultiplier = 1.0;
|
||||
|
||||
event.setDamage(boostedDamage);
|
||||
processCombatXP(mcMMOPlayer, target, PrimarySkillType.CROSSBOWS, forceMultiplier * distanceMultiplier);
|
||||
|
||||
printFinalDamageDebug(player, event, mcMMOPlayer,
|
||||
"Distance Multiplier: "+distanceMultiplier,
|
||||
"Force Multiplier: "+forceMultiplier,
|
||||
"Initial Damage: "+initialDamage,
|
||||
"Final Damage: "+boostedDamage);
|
||||
|
||||
//Clean data
|
||||
delayArrowMetaCleanup(arrow);
|
||||
}
|
||||
|
||||
private static void processAxeCombat(@NotNull LivingEntity target, @NotNull Player player, @NotNull EntityDamageByEntityEvent event) {
|
||||
if (event.getCause() == DamageCause.THORNS) {
|
||||
@ -240,14 +311,15 @@ public final class CombatUtils {
|
||||
|
||||
}
|
||||
|
||||
private static void processArcheryCombat(@NotNull LivingEntity target, @NotNull Player player, @NotNull EntityDamageByEntityEvent event, @NotNull Projectile arrow) {
|
||||
private static void processArcheryCombat(@NotNull LivingEntity target, @NotNull Player player,
|
||||
@NotNull EntityDamageByEntityEvent event, @NotNull Arrow arrow) {
|
||||
double initialDamage = event.getDamage();
|
||||
|
||||
McMMOPlayer mcMMOPlayer = UserManager.getPlayer(player);
|
||||
|
||||
//Make sure the profiles been loaded
|
||||
if(mcMMOPlayer == null) {
|
||||
cleanupArrowMetadata(arrow);
|
||||
delayArrowMetaCleanup(arrow);
|
||||
return;
|
||||
}
|
||||
|
||||
@ -273,7 +345,7 @@ public final class CombatUtils {
|
||||
boostedDamage+=getLimitBreakDamage(player, target, SubSkillType.ARCHERY_ARCHERY_LIMIT_BREAK);
|
||||
}
|
||||
|
||||
double distanceMultiplier = archeryManager.distanceXpBonusMultiplier(target, arrow);
|
||||
double distanceMultiplier = ArcheryManager.distanceXpBonusMultiplier(target, arrow);
|
||||
double forceMultiplier = 1.0; //Hacky Fix - some plugins spawn arrows and assign them to players after the ProjectileLaunchEvent fires
|
||||
|
||||
if(arrow.hasMetadata(MetadataConstants.METADATA_KEY_BOW_FORCE))
|
||||
@ -288,7 +360,7 @@ public final class CombatUtils {
|
||||
"Initial Damage: "+initialDamage,
|
||||
"Final Damage: "+boostedDamage);
|
||||
//Clean data
|
||||
cleanupArrowMetadata(arrow);
|
||||
delayArrowMetaCleanup(arrow);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -387,6 +459,15 @@ public final class CombatUtils {
|
||||
processUnarmedCombat(target, player, event);
|
||||
}
|
||||
}
|
||||
else if (ItemUtils.isTrident(heldItem)) {
|
||||
if (!mcMMO.p.getSkillTools().canCombatSkillsTrigger(PrimarySkillType.TRIDENTS, target)) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (mcMMO.p.getSkillTools().doesPlayerHaveSkillPermission(player, PrimarySkillType.TRIDENTS)) {
|
||||
processTridentCombat(target, player, event);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
else if (entityType == EntityType.WOLF) {
|
||||
@ -400,20 +481,25 @@ public final class CombatUtils {
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (entityType == EntityType.ARROW || entityType == EntityType.SPECTRAL_ARROW) {
|
||||
Projectile arrow = (Projectile) painSource;
|
||||
else if (painSource instanceof Arrow arrow) {
|
||||
ProjectileSource projectileSource = arrow.getShooter();
|
||||
boolean isCrossbow = arrow.isShotFromCrossbow();
|
||||
if (projectileSource instanceof Player player) {
|
||||
|
||||
if (projectileSource instanceof Player player && mcMMO.p.getSkillTools().canCombatSkillsTrigger(PrimarySkillType.ARCHERY, target)) {
|
||||
|
||||
if (!Misc.isNPCEntityExcludingVillagers(player) && mcMMO.p.getSkillTools().doesPlayerHaveSkillPermission(player, PrimarySkillType.ARCHERY)) {
|
||||
processArcheryCombat(target, player, event, arrow);
|
||||
if (!Misc.isNPCEntityExcludingVillagers(player)) {
|
||||
if(!isCrossbow && mcMMO.p.getSkillTools().canCombatSkillsTrigger(PrimarySkillType.ARCHERY, target)) {
|
||||
processArcheryCombat(target, player, event, arrow);
|
||||
} else if(isCrossbow && mcMMO.p.getSkillTools().canCombatSkillsTrigger(PrimarySkillType.CROSSBOWS, target)) {
|
||||
processCrossbowsCombat(target, player, event, arrow);
|
||||
}
|
||||
} else {
|
||||
//Cleanup Arrow
|
||||
cleanupArrowMetadata(arrow);
|
||||
delayArrowMetaCleanup(arrow);
|
||||
}
|
||||
|
||||
if (target.getType() != EntityType.CREEPER && !Misc.isNPCEntityExcludingVillagers(player) && mcMMO.p.getSkillTools().doesPlayerHaveSkillPermission(player, PrimarySkillType.TAMING)) {
|
||||
if (target.getType() != EntityType.CREEPER
|
||||
&& !Misc.isNPCEntityExcludingVillagers(player)
|
||||
&& mcMMO.p.getSkillTools().doesPlayerHaveSkillPermission(player, PrimarySkillType.TAMING)) {
|
||||
McMMOPlayer mcMMOPlayer = UserManager.getPlayer(player);
|
||||
|
||||
if(mcMMOPlayer == null)
|
||||
@ -424,7 +510,6 @@ public final class CombatUtils {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
@ -625,7 +710,7 @@ public final class CombatUtils {
|
||||
}
|
||||
|
||||
public static boolean hasIgnoreDamageMetadata(@NotNull LivingEntity target) {
|
||||
return target.getMetadata(MetadataConstants.METADATA_KEY_CUSTOM_DAMAGE).size() != 0;
|
||||
return target.hasMetadata(MetadataConstants.METADATA_KEY_CUSTOM_DAMAGE);
|
||||
}
|
||||
|
||||
public static void dealNoInvulnerabilityTickDamageRupture(@NotNull LivingEntity target, double damage, Entity attacker, int toolTier) {
|
||||
@ -634,35 +719,6 @@ public final class CombatUtils {
|
||||
}
|
||||
|
||||
dealNoInvulnerabilityTickDamage(target, damage, attacker);
|
||||
|
||||
// //IFrame storage
|
||||
//// int noDamageTicks = target.getNoDamageTicks();
|
||||
//
|
||||
//// String debug = "BLEED DMG RESULT: INC DMG:"+damage+", HP-Before:"+target.getHealth()+", HP-After:";
|
||||
//
|
||||
//// double incDmg = getFakeDamageFinalResult(attacker, target, DamageCause.ENTITY_ATTACK, damage);
|
||||
//
|
||||
//// double newHealth = Math.max(0, target.getHealth() - incDmg);
|
||||
//
|
||||
// //Don't kill things with a stone or wooden weapon
|
||||
//// if(toolTier < 3 && newHealth == 0)
|
||||
//// return;
|
||||
//
|
||||
// target.setMetadata(mcMMO.CUSTOM_DAMAGE_METAKEY, mcMMO.metadataValue);
|
||||
//
|
||||
// if(newHealth == 0 && !(target instanceof Player))
|
||||
// {
|
||||
// target.damage(99999, attacker);
|
||||
// }
|
||||
// else
|
||||
// {
|
||||
//// Vector beforeRuptureVec = new Vector(target.getVelocity().getX(), target.getVelocity().getY(), target.getVelocity().getZ()); ;
|
||||
// target.damage(damage, attacker);
|
||||
//// debug+=target.getHealth();
|
||||
// Bukkit.broadcastMessage(debug);
|
||||
//// target.setNoDamageTicks(noDamageTicks); //Do not add additional IFrames
|
||||
//// target.setVelocity(beforeRuptureVec);
|
||||
// }
|
||||
}
|
||||
|
||||
/**
|
||||
@ -745,14 +801,16 @@ public final class CombatUtils {
|
||||
XPGainReason xpGainReason;
|
||||
|
||||
if (target instanceof Player defender) {
|
||||
if (!ExperienceConfig.getInstance().getExperienceGainsPlayerVersusPlayerEnabled() ||
|
||||
if (!ExperienceConfig.getInstance().getExperienceGainsPlayerVersusPlayerEnabled()
|
||||
||
|
||||
(mcMMO.p.getPartyConfig().isPartyEnabled() && mcMMO.p.getPartyManager().inSameParty(mcMMOPlayer.getPlayer(), (Player) target))) {
|
||||
return;
|
||||
}
|
||||
|
||||
xpGainReason = XPGainReason.PVP;
|
||||
|
||||
if (defender.isOnline() && SkillUtils.cooldownExpired(mcMMOPlayer.getRespawnATS(), Misc.PLAYER_RESPAWN_COOLDOWN_SECONDS)) {
|
||||
if (defender.isOnline()
|
||||
&& SkillUtils.cooldownExpired(mcMMOPlayer.getRespawnATS(), Misc.PLAYER_RESPAWN_COOLDOWN_SECONDS)) {
|
||||
baseXP = 20 * ExperienceConfig.getInstance().getPlayerVersusPlayerXP();
|
||||
}
|
||||
}
|
||||
@ -811,7 +869,7 @@ public final class CombatUtils {
|
||||
|
||||
baseXP *= multiplier;
|
||||
|
||||
if (baseXP != 0) {
|
||||
if (baseXP > 0) {
|
||||
mcMMO.p.getFoliaLib().getImpl().runAtEntity(mcMMOPlayer.getPlayer(), new AwardCombatXpTask(mcMMOPlayer, primarySkillType, baseXP, target, xpGainReason));
|
||||
}
|
||||
}
|
||||
@ -949,31 +1007,12 @@ public final class CombatUtils {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Clean up metadata from a projectile
|
||||
*
|
||||
* @param entity projectile
|
||||
*/
|
||||
public static void cleanupArrowMetadata(@NotNull Projectile entity) {
|
||||
if(entity.hasMetadata(MetadataConstants.METADATA_KEY_INF_ARROW)) {
|
||||
entity.removeMetadata(MetadataConstants.METADATA_KEY_INF_ARROW, mcMMO.p);
|
||||
}
|
||||
|
||||
if(entity.hasMetadata(MetadataConstants.METADATA_KEY_BOW_FORCE)) {
|
||||
entity.removeMetadata(MetadataConstants.METADATA_KEY_BOW_FORCE, mcMMO.p);
|
||||
}
|
||||
|
||||
if(entity.hasMetadata(MetadataConstants.METADATA_KEY_ARROW_DISTANCE)) {
|
||||
entity.removeMetadata(MetadataConstants.METADATA_KEY_ARROW_DISTANCE, mcMMO.p);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Clean up metadata from a projectile after a minute has passed
|
||||
*
|
||||
* @param entity the projectile
|
||||
* @param arrow the projectile
|
||||
*/
|
||||
public static void delayArrowMetaCleanup(@NotNull Projectile entity) {
|
||||
mcMMO.p.getFoliaLib().getImpl().runLater(() -> cleanupArrowMetadata(entity), 20*60);
|
||||
public static void delayArrowMetaCleanup(@NotNull Arrow arrow) {
|
||||
mcMMO.p.getFoliaLib().getImpl().runLater(() -> ProjectileUtils.cleanupProjectileMetadata(arrow), 20*120);
|
||||
}
|
||||
}
|
||||
|
@ -0,0 +1,84 @@
|
||||
package com.gmail.nossr50.util.skills;
|
||||
|
||||
import com.gmail.nossr50.mcMMO;
|
||||
import com.gmail.nossr50.util.MetadataConstants;
|
||||
import org.bukkit.block.BlockFace;
|
||||
import org.bukkit.entity.Arrow;
|
||||
import org.bukkit.metadata.FixedMetadataValue;
|
||||
import org.bukkit.plugin.Plugin;
|
||||
import org.bukkit.util.Vector;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
public class ProjectileUtils {
|
||||
public static Vector getNormal(BlockFace blockFace) {
|
||||
return switch (blockFace) {
|
||||
case UP -> new Vector(0, 1, 0);
|
||||
case DOWN -> new Vector(0, -1, 0);
|
||||
case NORTH -> new Vector(0, 0, -1);
|
||||
case SOUTH -> new Vector(0, 0, 1);
|
||||
case EAST -> new Vector(1, 0, 0);
|
||||
case WEST -> new Vector(-1, 0, 0);
|
||||
default -> new Vector(0, 0, 0);
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Clean up all possible mcMMO related metadata for a projectile
|
||||
*
|
||||
* @param arrow projectile
|
||||
*/
|
||||
// TODO: Add test
|
||||
public static void cleanupProjectileMetadata(@NotNull Arrow arrow) {
|
||||
if(arrow.hasMetadata(MetadataConstants.METADATA_KEY_INF_ARROW)) {
|
||||
arrow.removeMetadata(MetadataConstants.METADATA_KEY_INF_ARROW, mcMMO.p);
|
||||
}
|
||||
|
||||
if(arrow.hasMetadata(MetadataConstants.METADATA_KEY_BOW_FORCE)) {
|
||||
arrow.removeMetadata(MetadataConstants.METADATA_KEY_BOW_FORCE, mcMMO.p);
|
||||
}
|
||||
|
||||
if(arrow.hasMetadata(MetadataConstants.METADATA_KEY_ARROW_DISTANCE)) {
|
||||
arrow.removeMetadata(MetadataConstants.METADATA_KEY_ARROW_DISTANCE, mcMMO.p);
|
||||
}
|
||||
|
||||
if(arrow.hasMetadata(MetadataConstants.METADATA_KEY_SPAWNED_ARROW)) {
|
||||
arrow.removeMetadata(MetadataConstants.METADATA_KEY_SPAWNED_ARROW, mcMMO.p);
|
||||
}
|
||||
|
||||
if(arrow.hasMetadata(MetadataConstants.METADATA_KEY_MULTI_SHOT_ARROW)) {
|
||||
arrow.removeMetadata(MetadataConstants.METADATA_KEY_MULTI_SHOT_ARROW, mcMMO.p);
|
||||
}
|
||||
|
||||
if(arrow.hasMetadata(MetadataConstants.METADATA_KEY_BOUNCE_COUNT)) {
|
||||
arrow.removeMetadata(MetadataConstants.METADATA_KEY_BOUNCE_COUNT, mcMMO.p);
|
||||
}
|
||||
}
|
||||
|
||||
public static void copyArrowMetadata(@NotNull Plugin pluginRef, @NotNull Arrow arrowToCopy, @NotNull Arrow newArrow) {
|
||||
if(arrowToCopy.hasMetadata(MetadataConstants.METADATA_KEY_INF_ARROW)) {
|
||||
newArrow.setMetadata(MetadataConstants.METADATA_KEY_INF_ARROW,
|
||||
arrowToCopy.getMetadata(MetadataConstants.METADATA_KEY_INF_ARROW).get(0));
|
||||
}
|
||||
|
||||
if(arrowToCopy.hasMetadata(MetadataConstants.METADATA_KEY_BOW_FORCE)) {
|
||||
newArrow.setMetadata(MetadataConstants.METADATA_KEY_BOW_FORCE,
|
||||
new FixedMetadataValue(pluginRef,
|
||||
arrowToCopy.getMetadata(MetadataConstants.METADATA_KEY_BOW_FORCE).get(0).asDouble()));
|
||||
}
|
||||
|
||||
if(arrowToCopy.hasMetadata(MetadataConstants.METADATA_KEY_ARROW_DISTANCE)) {
|
||||
newArrow.setMetadata(MetadataConstants.METADATA_KEY_ARROW_DISTANCE,
|
||||
arrowToCopy.getMetadata(MetadataConstants.METADATA_KEY_ARROW_DISTANCE).get(0));
|
||||
}
|
||||
|
||||
if(arrowToCopy.hasMetadata(MetadataConstants.METADATA_KEY_SPAWNED_ARROW)) {
|
||||
newArrow.setMetadata(MetadataConstants.METADATA_KEY_SPAWNED_ARROW,
|
||||
arrowToCopy.getMetadata(MetadataConstants.METADATA_KEY_SPAWNED_ARROW).get(0));
|
||||
}
|
||||
|
||||
if(arrowToCopy.hasMetadata(MetadataConstants.METADATA_KEY_MULTI_SHOT_ARROW)) {
|
||||
newArrow.setMetadata(MetadataConstants.METADATA_KEY_MULTI_SHOT_ARROW,
|
||||
arrowToCopy.getMetadata(MetadataConstants.METADATA_KEY_MULTI_SHOT_ARROW).get(0));
|
||||
}
|
||||
}
|
||||
}
|
@ -24,7 +24,7 @@ public class RankUtils {
|
||||
*
|
||||
* @param plugin plugin instance ref
|
||||
* @param mcMMOPlayer target player
|
||||
* @param primarySkillType
|
||||
* @param primarySkillType the skill to check
|
||||
* @param newLevel the new level of this skill
|
||||
*/
|
||||
public static void executeSkillUnlockNotifications(Plugin plugin, McMMOPlayer mcMMOPlayer, PrimarySkillType primarySkillType, int newLevel)
|
||||
@ -55,6 +55,9 @@ public class RankUtils {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Reset the interval between skill unlock notifications
|
||||
*/
|
||||
public static void resetUnlockDelayTimer()
|
||||
{
|
||||
count = 0;
|
||||
|
@ -30,6 +30,8 @@ public class SkillTools {
|
||||
public final @NotNull ImmutableSet<String> EXACT_SUBSKILL_NAMES;
|
||||
public final @NotNull ImmutableList<PrimarySkillType> CHILD_SKILLS;
|
||||
public final static @NotNull ImmutableList<PrimarySkillType> NON_CHILD_SKILLS;
|
||||
public final static @NotNull ImmutableList<PrimarySkillType> SALVAGE_PARENTS;
|
||||
public final static @NotNull ImmutableList<PrimarySkillType> SMELTING_PARENTS;
|
||||
public final @NotNull ImmutableList<PrimarySkillType> COMBAT_SKILLS;
|
||||
public final @NotNull ImmutableList<PrimarySkillType> GATHERING_SKILLS;
|
||||
public final @NotNull ImmutableList<PrimarySkillType> MISC_SKILLS;
|
||||
@ -50,9 +52,11 @@ public class SkillTools {
|
||||
}
|
||||
|
||||
NON_CHILD_SKILLS = ImmutableList.copyOf(tempNonChildSkills);
|
||||
SALVAGE_PARENTS = ImmutableList.of(PrimarySkillType.REPAIR, PrimarySkillType.FISHING);
|
||||
SMELTING_PARENTS = ImmutableList.of(PrimarySkillType.MINING, PrimarySkillType.REPAIR);
|
||||
}
|
||||
|
||||
public SkillTools(@NotNull mcMMO pluginRef) {
|
||||
public SkillTools(@NotNull mcMMO pluginRef) throws InvalidSkillException {
|
||||
this.pluginRef = pluginRef;
|
||||
|
||||
/*
|
||||
@ -140,26 +144,38 @@ public class SkillTools {
|
||||
*/
|
||||
|
||||
List<PrimarySkillType> childSkills = new ArrayList<>();
|
||||
// List<PrimarySkillType> nonChildSkills = new ArrayList<>();
|
||||
|
||||
for (PrimarySkillType primarySkillType : PrimarySkillType.values()) {
|
||||
if (isChildSkill(primarySkillType))
|
||||
childSkills.add(primarySkillType);
|
||||
// } {
|
||||
// nonChildSkills.add(primarySkillType);
|
||||
// }
|
||||
}
|
||||
|
||||
CHILD_SKILLS = ImmutableList.copyOf(childSkills);
|
||||
// NON_CHILD_SKILLS = ImmutableList.copyOf(nonChildSkills);
|
||||
|
||||
/*
|
||||
* Build categorized skill lists
|
||||
*/
|
||||
|
||||
COMBAT_SKILLS = ImmutableList.of(PrimarySkillType.ARCHERY, PrimarySkillType.AXES, PrimarySkillType.SWORDS, PrimarySkillType.TAMING, PrimarySkillType.UNARMED);
|
||||
GATHERING_SKILLS = ImmutableList.of(PrimarySkillType.EXCAVATION, PrimarySkillType.FISHING, PrimarySkillType.HERBALISM, PrimarySkillType.MINING, PrimarySkillType.WOODCUTTING);
|
||||
MISC_SKILLS = ImmutableList.of(PrimarySkillType.ACROBATICS, PrimarySkillType.ALCHEMY, PrimarySkillType.REPAIR, PrimarySkillType.SALVAGE, PrimarySkillType.SMELTING);
|
||||
COMBAT_SKILLS = ImmutableList.of(
|
||||
PrimarySkillType.ARCHERY,
|
||||
PrimarySkillType.AXES,
|
||||
PrimarySkillType.CROSSBOWS,
|
||||
PrimarySkillType.SWORDS,
|
||||
PrimarySkillType.TAMING,
|
||||
PrimarySkillType.TRIDENTS,
|
||||
PrimarySkillType.UNARMED);
|
||||
GATHERING_SKILLS = ImmutableList.of(
|
||||
PrimarySkillType.EXCAVATION,
|
||||
PrimarySkillType.FISHING,
|
||||
PrimarySkillType.HERBALISM,
|
||||
PrimarySkillType.MINING,
|
||||
PrimarySkillType.WOODCUTTING);
|
||||
MISC_SKILLS = ImmutableList.of(
|
||||
PrimarySkillType.ACROBATICS,
|
||||
PrimarySkillType.ALCHEMY,
|
||||
PrimarySkillType.REPAIR,
|
||||
PrimarySkillType.SALVAGE,
|
||||
PrimarySkillType.SMELTING);
|
||||
|
||||
/*
|
||||
* Build formatted/localized/etc string lists
|
||||
@ -171,25 +187,18 @@ public class SkillTools {
|
||||
}
|
||||
|
||||
private @NotNull PrimarySkillType getSuperAbilityParent(SuperAbilityType superAbilityType) throws InvalidSkillException {
|
||||
switch(superAbilityType) {
|
||||
case BERSERK:
|
||||
return PrimarySkillType.UNARMED;
|
||||
case GREEN_TERRA:
|
||||
return PrimarySkillType.HERBALISM;
|
||||
case TREE_FELLER:
|
||||
return PrimarySkillType.WOODCUTTING;
|
||||
case SUPER_BREAKER:
|
||||
case BLAST_MINING:
|
||||
return PrimarySkillType.MINING;
|
||||
case SKULL_SPLITTER:
|
||||
return PrimarySkillType.AXES;
|
||||
case SERRATED_STRIKES:
|
||||
return PrimarySkillType.SWORDS;
|
||||
case GIGA_DRILL_BREAKER:
|
||||
return PrimarySkillType.EXCAVATION;
|
||||
default:
|
||||
throw new InvalidSkillException("No parent defined for super ability! "+superAbilityType.toString());
|
||||
}
|
||||
return switch (superAbilityType) {
|
||||
case BERSERK -> PrimarySkillType.UNARMED;
|
||||
case GREEN_TERRA -> PrimarySkillType.HERBALISM;
|
||||
case TREE_FELLER -> PrimarySkillType.WOODCUTTING;
|
||||
case SUPER_BREAKER, BLAST_MINING -> PrimarySkillType.MINING;
|
||||
case SKULL_SPLITTER -> PrimarySkillType.AXES;
|
||||
case SERRATED_STRIKES -> PrimarySkillType.SWORDS;
|
||||
case GIGA_DRILL_BREAKER -> PrimarySkillType.EXCAVATION;
|
||||
case SUPER_SHOTGUN -> PrimarySkillType.CROSSBOWS;
|
||||
case TRIDENTS_SUPER_ABILITY -> PrimarySkillType.TRIDENTS;
|
||||
case EXPLOSIVE_SHOT -> PrimarySkillType.ARCHERY;
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
@ -319,7 +328,6 @@ public class SkillTools {
|
||||
}
|
||||
|
||||
public Set<SubSkillType> getSubSkills(PrimarySkillType primarySkillType) {
|
||||
//TODO: Cache this!
|
||||
return primarySkillChildrenMap.get(primarySkillType);
|
||||
}
|
||||
|
||||
@ -329,14 +337,10 @@ public class SkillTools {
|
||||
|
||||
// TODO: This is a little "hacky", we probably need to add something to distinguish child skills in the enum, or to use another enum for them
|
||||
public static boolean isChildSkill(PrimarySkillType primarySkillType) {
|
||||
switch (primarySkillType) {
|
||||
case SALVAGE:
|
||||
case SMELTING:
|
||||
return true;
|
||||
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
return switch (primarySkillType) {
|
||||
case SALVAGE, SMELTING -> true;
|
||||
default -> false;
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
@ -401,34 +405,7 @@ public class SkillTools {
|
||||
* @return true if the player has permissions, false otherwise
|
||||
*/
|
||||
public boolean superAbilityPermissionCheck(SuperAbilityType superAbilityType, Player player) {
|
||||
switch (superAbilityType) {
|
||||
case BERSERK:
|
||||
return Permissions.berserk(player);
|
||||
|
||||
case BLAST_MINING:
|
||||
return Permissions.remoteDetonation(player);
|
||||
|
||||
case GIGA_DRILL_BREAKER:
|
||||
return Permissions.gigaDrillBreaker(player);
|
||||
|
||||
case GREEN_TERRA:
|
||||
return Permissions.greenTerra(player);
|
||||
|
||||
case SERRATED_STRIKES:
|
||||
return Permissions.serratedStrikes(player);
|
||||
|
||||
case SKULL_SPLITTER:
|
||||
return Permissions.skullSplitter(player);
|
||||
|
||||
case SUPER_BREAKER:
|
||||
return Permissions.superBreaker(player);
|
||||
|
||||
case TREE_FELLER:
|
||||
return Permissions.treeFeller(player);
|
||||
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
return superAbilityType.getPermissions(player);
|
||||
}
|
||||
|
||||
public @NotNull List<PrimarySkillType> getChildSkills() {
|
||||
@ -450,4 +427,17 @@ public class SkillTools {
|
||||
public @NotNull ImmutableList<PrimarySkillType> getMiscSkills() {
|
||||
return MISC_SKILLS;
|
||||
}
|
||||
|
||||
public @NotNull ImmutableList<PrimarySkillType> getChildSkillParents(PrimarySkillType childSkill)
|
||||
throws IllegalArgumentException {
|
||||
switch (childSkill) {
|
||||
case SALVAGE -> {
|
||||
return SALVAGE_PARENTS;
|
||||
}
|
||||
case SMELTING -> {
|
||||
return SMELTING_PARENTS;
|
||||
}
|
||||
default -> throw new IllegalArgumentException("Skill " + childSkill + " is not a child skill");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -13,6 +13,7 @@ import com.gmail.nossr50.mcMMO;
|
||||
import com.gmail.nossr50.metadata.ItemMetadataService;
|
||||
import com.gmail.nossr50.util.ItemUtils;
|
||||
import com.gmail.nossr50.util.Misc;
|
||||
import com.gmail.nossr50.util.Permissions;
|
||||
import com.gmail.nossr50.util.player.NotificationManager;
|
||||
import com.gmail.nossr50.util.player.UserManager;
|
||||
import com.gmail.nossr50.util.text.StringUtils;
|
||||
@ -33,6 +34,8 @@ import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import java.util.Iterator;
|
||||
|
||||
import static java.util.Objects.requireNonNull;
|
||||
|
||||
public final class SkillUtils {
|
||||
/**
|
||||
* This is a static utility class, therefore we don't want any instances of
|
||||
@ -264,8 +267,8 @@ public final class SkillUtils {
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Modify the durability of an ItemStack, using Armor specific formula for unbreaking enchant damage reduction
|
||||
*
|
||||
@ -352,4 +355,14 @@ public final class SkillUtils {
|
||||
|
||||
return quantity;
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if a player can use a skill
|
||||
* @param player target player
|
||||
* @param subSkillType target subskill
|
||||
* @return true if the player has permission and has the skill unlocked
|
||||
*/
|
||||
public static boolean canUseSubskill(Player player, @NotNull SubSkillType subSkillType) {
|
||||
return Permissions.isSubSkillEnabled(player, subSkillType) && RankUtils.hasUnlockedSubskill(player, subSkillType);
|
||||
}
|
||||
}
|
||||
|
@ -133,38 +133,37 @@ public class TextComponentFactory {
|
||||
TextComponent.Builder webTextComponent;
|
||||
|
||||
switch (webLinks) {
|
||||
case WEBSITE:
|
||||
case WEBSITE -> {
|
||||
webTextComponent = Component.text().content(LocaleLoader.getString("JSON.Hover.AtSymbolURL"));
|
||||
TextUtils.addChildWebComponent(webTextComponent, "Web");
|
||||
webTextComponent.clickEvent(getUrlClickEvent(McMMOUrl.urlWebsite));
|
||||
break;
|
||||
case SPIGOT:
|
||||
}
|
||||
case SPIGOT -> {
|
||||
webTextComponent = Component.text().content(LocaleLoader.getString("JSON.Hover.AtSymbolURL"));
|
||||
TextUtils.addChildWebComponent(webTextComponent, "Spigot");
|
||||
webTextComponent.clickEvent(getUrlClickEvent(McMMOUrl.urlSpigot));
|
||||
break;
|
||||
case DISCORD:
|
||||
}
|
||||
case DISCORD -> {
|
||||
webTextComponent = Component.text().content(LocaleLoader.getString("JSON.Hover.AtSymbolURL"));
|
||||
TextUtils.addChildWebComponent(webTextComponent, "Discord");
|
||||
webTextComponent.clickEvent(getUrlClickEvent(McMMOUrl.urlDiscord));
|
||||
break;
|
||||
case PATREON:
|
||||
}
|
||||
case PATREON -> {
|
||||
webTextComponent = Component.text().content(LocaleLoader.getString("JSON.Hover.AtSymbolURL"));
|
||||
TextUtils.addChildWebComponent(webTextComponent, "Patreon");
|
||||
webTextComponent.clickEvent(getUrlClickEvent(McMMOUrl.urlPatreon));
|
||||
break;
|
||||
case WIKI:
|
||||
}
|
||||
case WIKI -> {
|
||||
webTextComponent = Component.text().content(LocaleLoader.getString("JSON.Hover.AtSymbolURL"));
|
||||
TextUtils.addChildWebComponent(webTextComponent, "Wiki");
|
||||
webTextComponent.clickEvent(getUrlClickEvent(McMMOUrl.urlWiki));
|
||||
break;
|
||||
case HELP_TRANSLATE:
|
||||
}
|
||||
case HELP_TRANSLATE -> {
|
||||
webTextComponent = Component.text().content(LocaleLoader.getString("JSON.Hover.AtSymbolURL"));
|
||||
TextUtils.addChildWebComponent(webTextComponent, "Lang");
|
||||
webTextComponent.clickEvent(getUrlClickEvent(McMMOUrl.urlTranslate));
|
||||
break;
|
||||
default:
|
||||
webTextComponent = Component.text().content("NOT DEFINED");
|
||||
}
|
||||
default -> webTextComponent = Component.text().content("NOT DEFINED");
|
||||
}
|
||||
|
||||
TextUtils.addNewHoverComponentToTextComponent(webTextComponent, getUrlHoverEvent(webLinks));
|
||||
@ -177,44 +176,45 @@ public class TextComponentFactory {
|
||||
TextComponent.Builder componentBuilder = Component.text().content(webLinks.getNiceTitle());
|
||||
|
||||
switch (webLinks) {
|
||||
case WEBSITE:
|
||||
case WEBSITE -> {
|
||||
addUrlHeaderHover(webLinks, componentBuilder);
|
||||
componentBuilder.append(Component.newline()).append(Component.newline());
|
||||
componentBuilder.append(Component.text(webLinks.getLocaleDescription(), NamedTextColor.GREEN));
|
||||
componentBuilder.append(Component.text("\nDev Blogs, and information related to mcMMO can be found here", NamedTextColor.GRAY));
|
||||
break;
|
||||
case SPIGOT:
|
||||
}
|
||||
case SPIGOT -> {
|
||||
addUrlHeaderHover(webLinks, componentBuilder);
|
||||
componentBuilder.append(Component.newline()).append(Component.newline());
|
||||
componentBuilder.append(Component.text(webLinks.getLocaleDescription(), NamedTextColor.GREEN));
|
||||
componentBuilder.append(Component.text("\nI post regularly in the discussion thread here!", NamedTextColor.GRAY));
|
||||
break;
|
||||
case PATREON:
|
||||
}
|
||||
case PATREON -> {
|
||||
addUrlHeaderHover(webLinks, componentBuilder);
|
||||
componentBuilder.append(Component.newline()).append(Component.newline());
|
||||
componentBuilder.append(Component.text(webLinks.getLocaleDescription(), NamedTextColor.GREEN));
|
||||
componentBuilder.append(Component.newline());
|
||||
componentBuilder.append(Component.text("Show support by buying me a coffee :)", NamedTextColor.GRAY));
|
||||
break;
|
||||
case WIKI:
|
||||
}
|
||||
case WIKI -> {
|
||||
addUrlHeaderHover(webLinks, componentBuilder);
|
||||
componentBuilder.append(Component.newline()).append(Component.newline());
|
||||
componentBuilder.append(Component.text(webLinks.getLocaleDescription(), NamedTextColor.GREEN));
|
||||
componentBuilder.append(Component.newline());
|
||||
componentBuilder.append(Component.text("I'm looking for more wiki staff, contact me on our discord!", NamedTextColor.DARK_GRAY));
|
||||
break;
|
||||
case DISCORD:
|
||||
}
|
||||
case DISCORD -> {
|
||||
addUrlHeaderHover(webLinks, componentBuilder);
|
||||
componentBuilder.append(Component.newline()).append(Component.newline());
|
||||
componentBuilder.append(Component.text(webLinks.getLocaleDescription(), NamedTextColor.GREEN));
|
||||
break;
|
||||
case HELP_TRANSLATE:
|
||||
}
|
||||
case HELP_TRANSLATE -> {
|
||||
addUrlHeaderHover(webLinks, componentBuilder);
|
||||
componentBuilder.append(Component.newline()).append(Component.newline());
|
||||
componentBuilder.append(Component.text(webLinks.getLocaleDescription(), NamedTextColor.GREEN));
|
||||
componentBuilder.append(Component.newline());
|
||||
componentBuilder.append(Component.text("You can use this website to help translate mcMMO into your language!" +
|
||||
"\nIf you want to know more contact me in discord.", NamedTextColor.DARK_GRAY));
|
||||
}
|
||||
}
|
||||
|
||||
return componentBuilder.build();
|
||||
@ -474,7 +474,7 @@ public class TextComponentFactory {
|
||||
/* NEW SKILL SYSTEM */
|
||||
for (AbstractSubSkill abstractSubSkill : InteractionManager.getSubSkillList()) {
|
||||
if (abstractSubSkill.getPrimarySkill() == parentSkill) {
|
||||
if (Permissions.isSubSkillEnabled(player, abstractSubSkill))
|
||||
if (Permissions.isSubSkillEnabled(player, abstractSubSkill.getSubSkillType()))
|
||||
textComponents.add(TextComponentFactory.getSubSkillTextComponent(player, abstractSubSkill));
|
||||
}
|
||||
}
|
||||
|
Reference in New Issue
Block a user