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());
}
diff --git a/src/main/java/com/gmail/nossr50/util/MetadataConstants.java b/src/main/java/com/gmail/nossr50/util/MetadataConstants.java
index 090e04d17..39f9479ce 100644
--- a/src/main/java/com/gmail/nossr50/util/MetadataConstants.java
+++ b/src/main/java/com/gmail/nossr50/util/MetadataConstants.java
@@ -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";
diff --git a/src/main/java/com/gmail/nossr50/util/Misc.java b/src/main/java/com/gmail/nossr50/util/Misc.java
index bbd9bb999..2467a5540 100644
--- a/src/main/java/com/gmail/nossr50/util/Misc.java
+++ b/src/main/java/com/gmail/nossr50/util/Misc.java
@@ -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);
+// }
+
}
diff --git a/src/main/java/com/gmail/nossr50/util/MobHealthbarUtils.java b/src/main/java/com/gmail/nossr50/util/MobHealthbarUtils.java
index 3d956afe7..5da29a656 100644
--- a/src/main/java/com/gmail/nossr50/util/MobHealthbarUtils.java
+++ b/src/main/java/com/gmail/nossr50/util/MobHealthbarUtils.java
@@ -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);
}
/**
diff --git a/src/main/java/com/gmail/nossr50/util/Permissions.java b/src/main/java/com/gmail/nossr50/util/Permissions.java
index c405f84ab..6dcd9b800 100644
--- a/src/main/java/com/gmail/nossr50/util/Permissions.java
+++ b/src/main/java/com/gmail/nossr50/util/Permissions.java
@@ -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);
+ }
}
diff --git a/src/main/java/com/gmail/nossr50/util/commands/CommandRegistrationManager.java b/src/main/java/com/gmail/nossr50/util/commands/CommandRegistrationManager.java
index 371124b06..0946b4cc5 100644
--- a/src/main/java/com/gmail/nossr50/util/commands/CommandRegistrationManager.java
+++ b/src/main/java/com/gmail/nossr50/util/commands/CommandRegistrationManager.java
@@ -42,8 +42,8 @@ public final class CommandRegistrationManager {
command.setDescription(LocaleLoader.getString("Commands.Description.Skill", StringUtils.getCapitalized(localizedName)));
command.setPermission("mcmmo.commands." + commandName);
command.setPermissionMessage(permissionsMessage);
- command.setUsage(LocaleLoader.getString("Commands.Usage.0", localizedName));
- command.setUsage(command.getUsage() + "\n" + LocaleLoader.getString("Commands.Usage.2", localizedName, "?", "[" + LocaleLoader.getString("Commands.Usage.Page") + "]"));
+ command.setUsage(LocaleLoader.getString("Commands.Usage.0", commandName));
+ command.setUsage(command.getUsage() + "\n" + LocaleLoader.getString("Commands.Usage.2", commandName, "?", "[" + LocaleLoader.getString("Commands.Usage.Page") + "]"));
switch (skill) {
case ACROBATICS:
@@ -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());
diff --git a/src/main/java/com/gmail/nossr50/util/random/InvalidActivationException.java b/src/main/java/com/gmail/nossr50/util/random/InvalidActivationException.java
deleted file mode 100644
index 677d3e63e..000000000
--- a/src/main/java/com/gmail/nossr50/util/random/InvalidActivationException.java
+++ /dev/null
@@ -1,5 +0,0 @@
-package com.gmail.nossr50.util.random;
-
-public class InvalidActivationException extends Exception {
- //Weee
-}
diff --git a/src/main/java/com/gmail/nossr50/util/random/Probability.java b/src/main/java/com/gmail/nossr50/util/random/Probability.java
new file mode 100644
index 000000000..3b9225b97
--- /dev/null
+++ b/src/main/java/com/gmail/nossr50/util/random/Probability.java
@@ -0,0 +1,97 @@
+package com.gmail.nossr50.util.random;
+
+import com.gmail.nossr50.api.exceptions.ValueOutOfBoundsException;
+import org.jetbrains.annotations.NotNull;
+
+import java.util.concurrent.ThreadLocalRandom;
+
+public interface Probability {
+ /**
+ * A Probability that always fails.
+ */
+ Probability ALWAYS_FAILS = () -> 0;
+
+ /**
+ * A Probability that always succeeds.
+ */
+ Probability ALWAYS_SUCCEEDS = () -> 1;
+
+ /**
+ * 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 of a percentage.
+ * This method takes a percentage and creates a Probability of equivalent odds.
+ *
+ * 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) {
+ 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
+ double probabilityValue = percentage / 100.0D;
+ return new ProbabilityImpl(probabilityValue);
+ }
+
+ /**
+ * Create a new Probability of a value.
+ * This method takes a value between 0 and 1 and creates a Probability of equivalent odds.
+ * 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.
+ * @param value the value of the probability
+ * @return a new Probability with the given value
+ */
+ static @NotNull Probability ofValue(double value) {
+ return new ProbabilityImpl(value);
+ }
+
+ /**
+ * 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);
+ }
+}
diff --git a/src/main/java/com/gmail/nossr50/util/random/ProbabilityImpl.java b/src/main/java/com/gmail/nossr50/util/random/ProbabilityImpl.java
new file mode 100644
index 000000000..b62110567
--- /dev/null
+++ b/src/main/java/com/gmail/nossr50/util/random/ProbabilityImpl.java
@@ -0,0 +1,53 @@
+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 from a static value.
+ * A value of 0 represents a 0% chance of success,
+ * A value of 1 represents a 100% chance of success.
+ * A value of 0.5 represents a 50% chance of success.
+ * A value of 0.01 represents a 1% chance of success.
+ * And so on.
+ *
+ * @param value the value of the probability between 0 and 100
+ */
+ public ProbabilityImpl(double value) throws ValueOutOfBoundsException {
+ if (value < 0) {
+ throw new ValueOutOfBoundsException("Value should never be negative for Probability!" +
+ " This suggests a coding mistake, contact the devs!");
+ }
+
+ probabilityValue = value;
+ }
+
+ @Override
+ public double getValue() {
+ return probabilityValue;
+ }
+
+ @Override
+ public String toString() {
+ return "ProbabilityImpl{" +
+ "probabilityValue=" + probabilityValue +
+ '}';
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) return true;
+ if (o == null || getClass() != o.getClass()) return false;
+ ProbabilityImpl that = (ProbabilityImpl) o;
+ return Double.compare(that.probabilityValue, probabilityValue) == 0;
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hashCode(probabilityValue);
+ }
+}
diff --git a/src/main/java/com/gmail/nossr50/util/random/ProbabilityUtil.java b/src/main/java/com/gmail/nossr50/util/random/ProbabilityUtil.java
new file mode 100644
index 000000000..4836efedc
--- /dev/null
+++ b/src/main/java/com/gmail/nossr50/util/random/ProbabilityUtil.java
@@ -0,0 +1,268 @@
+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 skillLevel;
+ double maxBonusLevel; // If a skill level is equal to the cap, it has the full probability
+
+ if (player != null) {
+ McMMOPlayer mmoPlayer = UserManager.getPlayer(player);
+ if (mmoPlayer == null) {
+ return Probability.ofPercent(0);
+ }
+ skillLevel = mmoPlayer.getSkillLevel(subSkillType.getParentSkill());
+ } else {
+ skillLevel = 0;
+ }
+
+ //Probability ceiling is configurable in this type
+ probabilityCeiling = mcMMO.p.getAdvancedConfig().getMaximumProbability(subSkillType);
+ //The xCeiling is configurable in this type
+ maxBonusLevel = mcMMO.p.getAdvancedConfig().getMaxBonusLevel(subSkillType);
+ return calculateCurrentSkillProbability(skillLevel, 0, probabilityCeiling, maxBonusLevel);
+ 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}
+ *
+ * 1) Determine where the RNG values come from for the passed {@link SubSkillType}
+ * NOTE: In the config file, there are values which are static and which are more dynamic, this is currently a bit hardcoded and will need to be updated manually
+ *
+ * 2) Determine whether to use Lucky multiplier and influence the outcome
+ *
+ * 3) Creates a {@link Probability} and pipes it to {@link ProbabilityUtil} which processes the result and returns it
+ *
+ * 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) {
+ final Probability probability = getSkillProbability(subSkillType, player);
+
+ //Luck
+ boolean isLucky = Permissions.lucky(player, subSkillType.getParentSkill());
+
+ if(isLucky) {
+ return probability.evaluate(LUCKY_MODIFIER);
+ } else {
+ return probability.evaluate();
+ }
+ }
+
+ /**
+ * Returns the {@link Probability} for a specific {@link SubSkillType} for a specific {@link Player}.
+ * This does not take into account perks such as lucky for the player.
+ * This is affected by other plugins who can listen to the {@link SubSkillEvent} and cancel it or mutate it.
+ *
+ * @param subSkillType the target subskill
+ * @param player the target player
+ * @return the probability for this skill
+ */
+ public static Probability getSkillProbability(@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 Probability.ALWAYS_FAILS;
+ }
+
+ //Result modifier
+ double resultModifier = subSkillEvent.getResultModifier();
+
+ //Mutate probability
+ if(resultModifier != 1.0D)
+ probability = Probability.ofPercent(probability.getValue() * resultModifier);
+
+ return 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 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)};
+ }
+
+ /**
+ * Helper function to calculate what probability a given skill has at a certain level
+ * @param skillLevel the skill level currently between the floor and the ceiling
+ * @param floor the minimum odds this skill can have
+ * @param ceiling the maximum odds this skill can have
+ * @param maxBonusLevel the maximum level this skill can have to reach the ceiling
+ *
+ * @return the probability of success for this skill at this level
+ */
+ public static Probability calculateCurrentSkillProbability(double skillLevel, double floor,
+ double ceiling, double maxBonusLevel) {
+ // The odds of success are between the value of the floor and the value of the ceiling.
+ // If the skill has a maxBonusLevel of 500 on this skill, then at skill level 500 you would have the full odds,
+ // at skill level 250 it would be half odds.
+
+ if (skillLevel >= maxBonusLevel || maxBonusLevel <= 0) {
+ // Avoid divide by zero bugs
+ // Max benefit has been reached, should always succeed
+ return Probability.ofPercent(ceiling);
+ }
+
+ double odds = (skillLevel / maxBonusLevel) * 100D;
+
+ // make sure the odds aren't lower or higher than the floor or ceiling
+ return Probability.ofPercent(Math.min(Math.max(floor, odds), ceiling));
+ }
+}
diff --git a/src/main/java/com/gmail/nossr50/util/random/RandomChanceExecution.java b/src/main/java/com/gmail/nossr50/util/random/RandomChanceExecution.java
deleted file mode 100644
index e5d51d740..000000000
--- a/src/main/java/com/gmail/nossr50/util/random/RandomChanceExecution.java
+++ /dev/null
@@ -1,18 +0,0 @@
-package com.gmail.nossr50.util.random;
-
-public interface RandomChanceExecution {
- /**
- * Gets the XPos used in the formula for success
- *
- * @return value of x for our success probability graph
- */
- double getXPos();
-
- /**
- * The maximum odds for this RandomChanceExecution
- * For example, if this value is 10, then 10% odds would be the maximum and would be achieved only when xPos equaled the LinearCurvePeak
- *
- * @return maximum probability odds from 0.00 (no chance of ever happened) to 100.0 (probability can be guaranteed)
- */
- double getProbabilityCap();
-}
diff --git a/src/main/java/com/gmail/nossr50/util/random/RandomChanceSkill.java b/src/main/java/com/gmail/nossr50/util/random/RandomChanceSkill.java
deleted file mode 100644
index 92d91ec83..000000000
--- a/src/main/java/com/gmail/nossr50/util/random/RandomChanceSkill.java
+++ /dev/null
@@ -1,176 +0,0 @@
-package com.gmail.nossr50.util.random;
-
-import com.gmail.nossr50.datatypes.player.McMMOPlayer;
-import com.gmail.nossr50.datatypes.skills.SubSkillType;
-import com.gmail.nossr50.util.Permissions;
-import com.gmail.nossr50.util.player.UserManager;
-import org.bukkit.entity.Player;
-import org.jetbrains.annotations.NotNull;
-import org.jetbrains.annotations.Nullable;
-
-public class RandomChanceSkill implements RandomChanceExecution {
- protected final double probabilityCap;
- protected final boolean isLucky;
- protected int skillLevel;
- protected final double resultModifier;
- protected final double maximumBonusLevelCap;
-
- public RandomChanceSkill(@Nullable Player player, @NotNull SubSkillType subSkillType, double resultModifier) {
- this.probabilityCap = RandomChanceUtil.LINEAR_CURVE_VAR;
-
- final McMMOPlayer mcMMOPlayer = UserManager.getPlayer(player);
- if (player != null && mcMMOPlayer != null) {
- this.skillLevel = mcMMOPlayer.getSkillLevel(subSkillType.getParentSkill());
- } else {
- this.skillLevel = 0;
- }
-
- if (player != null)
- isLucky = Permissions.lucky(player, subSkillType.getParentSkill());
- else
- isLucky = false;
-
- this.resultModifier = resultModifier;
- this.maximumBonusLevelCap = RandomChanceUtil.getMaxBonusLevelCap(subSkillType);
- }
-
- public RandomChanceSkill(@Nullable Player player, @NotNull SubSkillType subSkillType) {
- this.probabilityCap = RandomChanceUtil.LINEAR_CURVE_VAR;
-
- final McMMOPlayer mcMMOPlayer = UserManager.getPlayer(player);
- if (player != null && mcMMOPlayer != null) {
- this.skillLevel = mcMMOPlayer.getSkillLevel(subSkillType.getParentSkill());
- } else {
- this.skillLevel = 0;
- }
-
- if (player != null)
- isLucky = Permissions.lucky(player, subSkillType.getParentSkill());
- else
- isLucky = false;
-
- this.resultModifier = 1.0D;
- this.maximumBonusLevelCap = RandomChanceUtil.getMaxBonusLevelCap(subSkillType);
- }
-
- public RandomChanceSkill(@Nullable Player player, @NotNull SubSkillType subSkillType, boolean hasCap) {
- if (hasCap)
- this.probabilityCap = RandomChanceUtil.getMaximumProbability(subSkillType);
- else
- this.probabilityCap = RandomChanceUtil.LINEAR_CURVE_VAR;
-
- final McMMOPlayer mcMMOPlayer = UserManager.getPlayer(player);
- if (player != null && mcMMOPlayer != null) {
- this.skillLevel = mcMMOPlayer.getSkillLevel(subSkillType.getParentSkill());
- } else {
- this.skillLevel = 0;
- }
-
- if (player != null)
- isLucky = Permissions.lucky(player, subSkillType.getParentSkill());
- else
- isLucky = false;
-
- this.resultModifier = 1.0D;
- this.maximumBonusLevelCap = RandomChanceUtil.getMaxBonusLevelCap(subSkillType);
- }
-
- public RandomChanceSkill(@Nullable Player player, @NotNull SubSkillType subSkillType, boolean hasCap, boolean luckyOverride) {
- if (hasCap)
- this.probabilityCap = RandomChanceUtil.getMaximumProbability(subSkillType);
- else
- this.probabilityCap = RandomChanceUtil.LINEAR_CURVE_VAR;
-
- final McMMOPlayer mcMMOPlayer = UserManager.getPlayer(player);
- if (player != null && mcMMOPlayer != null) {
- this.skillLevel = mcMMOPlayer.getSkillLevel(subSkillType.getParentSkill());
- } else {
- this.skillLevel = 0;
- }
-
- isLucky = luckyOverride;
-
- this.resultModifier = 1.0D;
- this.maximumBonusLevelCap = RandomChanceUtil.getMaxBonusLevelCap(subSkillType);
- }
-
- public RandomChanceSkill(@Nullable Player player, @NotNull SubSkillType subSkillType, boolean hasCap, double resultModifier) {
- if (hasCap)
- this.probabilityCap = RandomChanceUtil.getMaximumProbability(subSkillType);
- else
- this.probabilityCap = RandomChanceUtil.LINEAR_CURVE_VAR;
-
- final McMMOPlayer mcMMOPlayer = UserManager.getPlayer(player);
- if (player != null && mcMMOPlayer != null) {
- this.skillLevel = mcMMOPlayer.getSkillLevel(subSkillType.getParentSkill());
- } else {
- this.skillLevel = 0;
- }
-
- if (player != null)
- isLucky = Permissions.lucky(player, subSkillType.getParentSkill());
- else
- isLucky = false;
-
- this.resultModifier = resultModifier;
- this.maximumBonusLevelCap = RandomChanceUtil.getMaxBonusLevelCap(subSkillType);
- }
-
- /**
- * Gets the skill level of the player who owns this RandomChanceSkill
- *
- * @return the current skill level relating to this RandomChanceSkill
- */
- public int getSkillLevel() {
- return skillLevel;
- }
-
- /**
- * Modify the skill level used for this skill's RNG calculations
- *
- * @param newSkillLevel new skill level
- */
- public void setSkillLevel(int newSkillLevel) {
- skillLevel = newSkillLevel;
- }
-
- /**
- * The maximum bonus level for this skill
- * This is when the skills level no longer increases the odds of success
- * For example, a value of 25 will mean the success chance no longer grows after skill level 25
- *
- * @return the maximum bonus from skill level for this skill
- */
- public double getMaximumBonusLevelCap() {
- return maximumBonusLevelCap;
- }
-
- /**
- * Gets the XPos used in the formula for success
- *
- * @return value of x for our success probability graph
- */
- @Override
- public double getXPos() {
- return getSkillLevel();
- }
-
- /**
- * The maximum odds for this RandomChanceExecution
- * For example, if this value is 10, then 10% odds would be the maximum and would be achieved only when xPos equaled the LinearCurvePeak
- *
- * @return maximum probability odds from 0.00 (no chance of ever happened) to 100.0 (probability can be guaranteed)
- */
- @Override
- public double getProbabilityCap() {
- return probabilityCap;
- }
-
- public boolean isLucky() {
- return isLucky;
- }
-
- public double getResultModifier() {
- return resultModifier;
- }
-}
diff --git a/src/main/java/com/gmail/nossr50/util/random/RandomChanceSkillStatic.java b/src/main/java/com/gmail/nossr50/util/random/RandomChanceSkillStatic.java
deleted file mode 100644
index c96b71d6b..000000000
--- a/src/main/java/com/gmail/nossr50/util/random/RandomChanceSkillStatic.java
+++ /dev/null
@@ -1,61 +0,0 @@
-package com.gmail.nossr50.util.random;
-
-import com.gmail.nossr50.datatypes.skills.SubSkillType;
-import org.bukkit.entity.Player;
-import org.jetbrains.annotations.NotNull;
-import org.jetbrains.annotations.Nullable;
-
-public class RandomChanceSkillStatic extends RandomChanceSkill {
- private final double xPos;
-
- public RandomChanceSkillStatic(double xPos, @Nullable Player player, @NotNull SubSkillType subSkillType) {
- super(player, subSkillType);
-
- this.xPos = xPos;
- }
-
- public RandomChanceSkillStatic(double xPos, @Nullable Player player, @NotNull SubSkillType subSkillType, boolean luckyOverride) {
- super(player, subSkillType, false, luckyOverride);
-
- this.xPos = xPos;
- }
-
- public RandomChanceSkillStatic(double xPos, @Nullable Player player, @NotNull SubSkillType subSkillType, double resultModifier) {
- super(player, subSkillType, resultModifier);
-
- this.xPos = xPos;
- }
-
- /**
- * Gets the XPos used in the formula for success
- *
- * @return value of x for our success probability graph
- */
- @Override
- public double getXPos() {
- return xPos;
- }
-
- /**
- * The maximum odds for this RandomChanceExecution
- * For example, if this value is 10, then 10% odds would be the maximum and would be achieved only when xPos equaled the LinearCurvePeak
- *
- * @return maximum probability odds from 0.00 (no chance of ever happened) to 100.0 (probability can be guaranteed)
- */
- @Override
- public double getProbabilityCap() {
- return probabilityCap;
- }
-
- /**
- * The maximum bonus level for this skill
- * This is when the skills level no longer increases the odds of success
- * For example, a value of 25 will mean the success chance no longer grows after skill level 25
- *
- * @return the maximum bonus from skill level for this skill
- */
- @Override
- public double getMaximumBonusLevelCap() {
- return 100;
- }
-}
diff --git a/src/main/java/com/gmail/nossr50/util/random/RandomChanceStatic.java b/src/main/java/com/gmail/nossr50/util/random/RandomChanceStatic.java
deleted file mode 100644
index 0b09a4a3c..000000000
--- a/src/main/java/com/gmail/nossr50/util/random/RandomChanceStatic.java
+++ /dev/null
@@ -1,38 +0,0 @@
-package com.gmail.nossr50.util.random;
-
-public class RandomChanceStatic implements RandomChanceExecution {
- private final double xPos;
- private final double probabilityCap;
- private final boolean isLucky;
-
- public RandomChanceStatic(double xPos, 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;
- }
-}
diff --git a/src/main/java/com/gmail/nossr50/util/random/RandomChanceUtil.java b/src/main/java/com/gmail/nossr50/util/random/RandomChanceUtil.java
deleted file mode 100644
index 0191172c1..000000000
--- a/src/main/java/com/gmail/nossr50/util/random/RandomChanceUtil.java
+++ /dev/null
@@ -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);
- }
-}
diff --git a/src/main/java/com/gmail/nossr50/util/random/SkillProbabilityType.java b/src/main/java/com/gmail/nossr50/util/random/SkillProbabilityType.java
new file mode 100644
index 000000000..95814f6a1
--- /dev/null
+++ b/src/main/java/com/gmail/nossr50/util/random/SkillProbabilityType.java
@@ -0,0 +1,6 @@
+package com.gmail.nossr50.util.random;
+
+public enum SkillProbabilityType {
+ DYNAMIC_CONFIGURABLE, //Has multiple values used for calculation (taken from config files)
+ STATIC_CONFIGURABLE, //A single value used for calculations (taken from config files)
+}
diff --git a/src/main/java/com/gmail/nossr50/util/scoreboards/ScoreboardWrapper.java b/src/main/java/com/gmail/nossr50/util/scoreboards/ScoreboardWrapper.java
index 1237ce086..4d2d51560 100644
--- a/src/main/java/com/gmail/nossr50/util/scoreboards/ScoreboardWrapper.java
+++ b/src/main/java/com/gmail/nossr50/util/scoreboards/ScoreboardWrapper.java
@@ -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));
}
}
diff --git a/src/main/java/com/gmail/nossr50/util/skills/CombatUtils.java b/src/main/java/com/gmail/nossr50/util/skills/CombatUtils.java
index 1d5eea33f..fd2d240c2 100644
--- a/src/main/java/com/gmail/nossr50/util/skills/CombatUtils.java
+++ b/src/main/java/com/gmail/nossr50/util/skills/CombatUtils.java
@@ -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,108 @@ public final class CombatUtils {
}
}
}
+ private static void processTridentCombatMelee(@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 processTridentCombatRanged(@NotNull Trident trident, @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 (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 +341,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 +375,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 +390,7 @@ public final class CombatUtils {
"Initial Damage: "+initialDamage,
"Final Damage: "+boostedDamage);
//Clean data
- cleanupArrowMetadata(arrow);
+ delayArrowMetaCleanup(arrow);
}
/**
@@ -300,6 +402,10 @@ public final class CombatUtils {
Entity painSource = event.getDamager();
EntityType entityType = painSource.getType();
+ if (target instanceof ArmorStand) {
+ return;
+ }
+
if (target instanceof Player player) {
if(ExperienceConfig.getInstance().isNPCInteractionPrevented()) {
if (Misc.isNPCEntityExcludingVillagers(target)) {
@@ -383,6 +489,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)) {
+ processTridentCombatMelee(target, player, event);
+ }
+ }
}
else if (entityType == EntityType.WOLF) {
@@ -396,20 +511,36 @@ public final class CombatUtils {
}
}
}
- else if (entityType == EntityType.ARROW || entityType == EntityType.SPECTRAL_ARROW) {
- Projectile arrow = (Projectile) painSource;
+ else if (painSource instanceof Trident trident) {
+ ProjectileSource projectileSource = trident.getShooter();
+
+ if (projectileSource instanceof Player player) {
+ if (!Misc.isNPCEntityExcludingVillagers(player)) {
+ if(mcMMO.p.getSkillTools().canCombatSkillsTrigger(PrimarySkillType.TRIDENTS, target)) {
+ processTridentCombatRanged(trident, target, player, event);
+ }
+ }
+ }
+ }
+ 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)
@@ -420,7 +551,6 @@ public final class CombatUtils {
}
}
}
-
}
/**
@@ -621,7 +751,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) {
@@ -630,35 +760,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);
-// }
}
/**
@@ -741,14 +842,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();
}
}
@@ -807,7 +910,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));
}
}
@@ -945,31 +1048,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);
}
}
diff --git a/src/main/java/com/gmail/nossr50/util/skills/ProjectileUtils.java b/src/main/java/com/gmail/nossr50/util/skills/ProjectileUtils.java
new file mode 100644
index 000000000..f64bac7a3
--- /dev/null
+++ b/src/main/java/com/gmail/nossr50/util/skills/ProjectileUtils.java
@@ -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));
+ }
+ }
+}
diff --git a/src/main/java/com/gmail/nossr50/util/skills/RankUtils.java b/src/main/java/com/gmail/nossr50/util/skills/RankUtils.java
index ec4d56481..e06497128 100644
--- a/src/main/java/com/gmail/nossr50/util/skills/RankUtils.java
+++ b/src/main/java/com/gmail/nossr50/util/skills/RankUtils.java
@@ -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;
diff --git a/src/main/java/com/gmail/nossr50/util/skills/SkillTools.java b/src/main/java/com/gmail/nossr50/util/skills/SkillTools.java
index 416926fe4..39ddfbd24 100644
--- a/src/main/java/com/gmail/nossr50/util/skills/SkillTools.java
+++ b/src/main/java/com/gmail/nossr50/util/skills/SkillTools.java
@@ -30,6 +30,8 @@ public class SkillTools {
public final @NotNull ImmutableSet EXACT_SUBSKILL_NAMES;
public final @NotNull ImmutableList CHILD_SKILLS;
public final static @NotNull ImmutableList NON_CHILD_SKILLS;
+ public final static @NotNull ImmutableList SALVAGE_PARENTS;
+ public final static @NotNull ImmutableList SMELTING_PARENTS;
public final @NotNull ImmutableList COMBAT_SKILLS;
public final @NotNull ImmutableList GATHERING_SKILLS;
public final @NotNull ImmutableList 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 childSkills = new ArrayList<>();
-// List 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,24 +328,19 @@ public class SkillTools {
}
public Set getSubSkills(PrimarySkillType primarySkillType) {
- //TODO: Cache this!
return primarySkillChildrenMap.get(primarySkillType);
}
- public double getXpModifier(PrimarySkillType primarySkillType) {
+ public double getXpMultiplier(PrimarySkillType primarySkillType) {
return ExperienceConfig.getInstance().getFormulaSkillModifier(primarySkillType);
}
// 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;
+ };
}
/**
@@ -345,8 +349,7 @@ public class SkillTools {
* @return the localized name for a {@link PrimarySkillType}
*/
public String getLocalizedSkillName(PrimarySkillType primarySkillType) {
- //TODO: Replace with current impl
- return StringUtils.getCapitalized(LocaleLoader.getString(StringUtils.getCapitalized(primarySkillType.toString()) + ".SkillName"));
+ return LocaleLoader.getString(StringUtils.getCapitalized(primarySkillType.toString()) + ".SkillName");
}
public boolean doesPlayerHaveSkillPermission(Player player, PrimarySkillType primarySkillType) {
@@ -401,34 +404,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 getChildSkills() {
@@ -450,4 +426,17 @@ public class SkillTools {
public @NotNull ImmutableList getMiscSkills() {
return MISC_SKILLS;
}
+
+ public @NotNull ImmutableList 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");
+ }
+ }
}
diff --git a/src/main/java/com/gmail/nossr50/util/skills/SkillUtils.java b/src/main/java/com/gmail/nossr50/util/skills/SkillUtils.java
index 6a15963ef..b0d406ae6 100644
--- a/src/main/java/com/gmail/nossr50/util/skills/SkillUtils.java
+++ b/src/main/java/com/gmail/nossr50/util/skills/SkillUtils.java
@@ -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;
@@ -264,8 +265,8 @@ public final class SkillUtils {
return false;
}
-
-
+
+
/**
* Modify the durability of an ItemStack, using Armor specific formula for unbreaking enchant damage reduction
*
@@ -352,4 +353,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);
+ }
}
diff --git a/src/main/java/com/gmail/nossr50/util/text/TextComponentFactory.java b/src/main/java/com/gmail/nossr50/util/text/TextComponentFactory.java
index 81ac8da28..89981bb85 100644
--- a/src/main/java/com/gmail/nossr50/util/text/TextComponentFactory.java
+++ b/src/main/java/com/gmail/nossr50/util/text/TextComponentFactory.java
@@ -19,6 +19,7 @@ import net.kyori.adventure.text.event.HoverEvent;
import net.kyori.adventure.text.format.NamedTextColor;
import net.kyori.adventure.text.format.TextColor;
import net.kyori.adventure.text.format.TextDecoration;
+import org.bukkit.ChatColor;
import org.bukkit.entity.Player;
import org.jetbrains.annotations.NotNull;
@@ -56,18 +57,23 @@ public class TextComponentFactory {
return Component.text(text);
}
- public static void sendPlayerSubSkillWikiLink(Player player, String subskillformatted) {
+ public static String getSubSkillWikiLink(SubSkillType subSkillType) {
+ return "https://wiki.mcmmo.org/en/skills/"
+ + subSkillType.getParentSkill().toString().toLowerCase(Locale.ENGLISH) + "#"
+ + subSkillType.getWikiUrl().toLowerCase(Locale.ENGLISH);
+ }
+
+ public static void sendPlayerSubSkillWikiLink(Player player, String subskillformatted, SubSkillType subSkillType) {
if (!mcMMO.p.getGeneralConfig().getUrlLinksEnabled())
return;
TextComponent.Builder wikiLinkComponent = Component.text().content(LocaleLoader.getString("Overhaul.mcMMO.MmoInfo.Wiki"));
wikiLinkComponent.decoration(TextDecoration.UNDERLINED, true);
- String wikiUrl = "https://wiki.mcmmo.org/" + subskillformatted;
+ final String subSkillWikiLink = getSubSkillWikiLink(subSkillType);
+ wikiLinkComponent.clickEvent(ClickEvent.openUrl(subSkillWikiLink));
- wikiLinkComponent.clickEvent(ClickEvent.openUrl(wikiUrl));
-
- TextComponent.Builder componentBuilder = Component.text().content(subskillformatted).append(Component.newline()).append(Component.text(wikiUrl)).color(NamedTextColor.GRAY).decoration(TextDecoration.ITALIC, true);
+ TextComponent.Builder componentBuilder = Component.text().content(subskillformatted).append(Component.newline()).append(Component.text(subSkillWikiLink)).color(NamedTextColor.GRAY).decoration(TextDecoration.ITALIC, true);
wikiLinkComponent.hoverEvent(HoverEvent.showText(componentBuilder.build()));
@@ -133,38 +139,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 +182,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();
@@ -230,8 +236,8 @@ public class TextComponentFactory {
}
private static Component getSubSkillTextComponent(Player player, SubSkillType subSkillType) {
- //Get skill name
- String skillName = subSkillType.getLocaleName();
+ //Get skill name and strip it of color
+ final String skillName = ChatColor.stripColor(subSkillType.getLocaleName());
boolean skillUnlocked = RankUtils.hasUnlockedSubskill(player, subSkillType);
@@ -301,7 +307,7 @@ public class TextComponentFactory {
* @return the hover basecomponent object for this subskill
*/
private static Component getSubSkillHoverEventJSON(AbstractSubSkill abstractSubSkill, Player player) {
- String skillName = abstractSubSkill.getNiceName();
+ String skillName = ChatColor.stripColor(abstractSubSkill.getNiceName());
/*
* Hover Event BaseComponent color table
@@ -394,7 +400,8 @@ public class TextComponentFactory {
}
private static Component getSubSkillHoverEventJSON(SubSkillType subSkillType, Player player) {
- String skillName = subSkillType.getLocaleName();
+ // Get skill name and strip it of color
+ String skillName = ChatColor.stripColor(subSkillType.getLocaleName());
/*
* Hover Event BaseComponent color table
@@ -428,11 +435,9 @@ public class TextComponentFactory {
}
componentBuilder.append(Component.newline());
- componentBuilder.append(Component.text(LocaleLoader.getString("JSON.DescriptionHeader")));
- componentBuilder.color(ccDescriptionHeader);
+ componentBuilder.append(Component.text(LocaleLoader.getString("JSON.DescriptionHeader")).color(ccDescriptionHeader));
componentBuilder.append(Component.newline());
- componentBuilder.append(Component.text(subSkillType.getLocaleDescription()));
- componentBuilder.color(ccDescription);
+ componentBuilder.append(Component.text(ChatColor.stripColor(subSkillType.getLocaleDescription())).color(ccDescription));
}
return componentBuilder.build();
@@ -474,7 +479,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));
}
}
diff --git a/src/main/resources/advanced.yml b/src/main/resources/advanced.yml
index 4e37db678..7d56c4e37 100644
--- a/src/main/resources/advanced.yml
+++ b/src/main/resources/advanced.yml
@@ -5,7 +5,7 @@
# For advanced users only! There is no need to change anything here.
#
# You can customize almost every aspect of every skill here.
-# Its mainly here if you've customized the experience formula.
+# It's mainly here if you've customized the experience formula.
# Configure at what level you get better with certain abilities.
#
#####
@@ -20,7 +20,7 @@ Feedback:
PlayerTips: true
SkillCommand:
BlankLinesAboveHeader: true
- # If sendtitles is true messages will be sent using the title api (BIG TEXT ON SCREEN)
+ # If sendtitles is true, messages will be sent using the title api (BIG TEXT ON SCREEN)
Events:
XP:
SendTitles: true
@@ -264,6 +264,11 @@ Skills:
MaxBonusLevel:
Standard: 100
RetroMode: 1000
+ VerdantBounty:
+ ChanceMax: 50.0
+ MaxBonusLevel:
+ Standard: 1000
+ RetroMode: 10000
HylianLuck:
# ChanceMax: Maximum chance of Hylian Luck when on or higher
@@ -284,6 +289,11 @@ Skills:
# Settings for Mining
###
Mining:
+ MotherLode:
+ MaxBonusLevel:
+ Standard: 1000
+ RetroMode: 10000
+ ChanceMax: 50.0
SuperBreaker:
AllowTripleDrops: true
DoubleDrops:
@@ -608,9 +618,16 @@ Skills:
Knock_On_Wood:
Add_XP_Orbs_To_Drops: true
+ # Triple Drops
+ CleanCuts:
+ # ChanceMax: Maximum chance of receiving triple drops (100 = 100%)
+ # MaxBonusLevel: Level when the maximum chance of receiving triple drops is reached
+ ChanceMax: 50.0
+ MaxBonusLevel:
+ Standard: 1000
+ RetroMode: 10000
# Double Drops
HarvestLumber:
- # ChanceMax & MaxBonusLevel are only used for Classic, I'll make that more clear in the future.
# ChanceMax: Maximum chance of receiving double drops (100 = 100%)
# MaxBonusLevel: Level when the maximum chance of receiving double drops is reached
ChanceMax: 100.0
diff --git a/src/main/resources/chat.yml b/src/main/resources/chat.yml
index 34f812a07..febf9c9f6 100644
--- a/src/main/resources/chat.yml
+++ b/src/main/resources/chat.yml
@@ -8,10 +8,12 @@ Chat:
Enable: true
# Whether to use the current display name of a player
Use_Display_Names: true
+ Send_To_Console: true
Spies:
# Whether players with the chat spy permission join the server with chat spying toggled on
Automatically_Enable_Spying: false
Admin:
+ Send_To_Console: true
# Enable or disable admin chat
Enable: true
# Whether to use the current display name of a player
diff --git a/src/main/resources/child.yml b/src/main/resources/child.yml
deleted file mode 100644
index 685a6ce71..000000000
--- a/src/main/resources/child.yml
+++ /dev/null
@@ -1,16 +0,0 @@
-#
-# mcMMO child skill configuration
-# Last updated on ${project.version}-b${BUILD_NUMBER}
-#
-# You do not need to modify this file except to change parents of child skills
-#
-# If you wish a child skill to be the parent of another child skill, you must also make your changes to the child.yml within the jar
-# WARNING: THIS IS NOT SUPPORTED, IF YOU DO SO YOU ARE RESPONSIBLE FOR THE ISSUES THAT MAY ARISE. That said, watch out for circular dependencies, those are bad.
-#
-#####
-Salvage:
- - Fishing
- - Repair
-Smelting:
- - Mining
- - Repair
\ No newline at end of file
diff --git a/src/main/resources/config.yml b/src/main/resources/config.yml
index 789729cc6..4df9d2eb0 100644
--- a/src/main/resources/config.yml
+++ b/src/main/resources/config.yml
@@ -49,6 +49,10 @@ General:
RetroMode:
Enabled: true
Locale: en_US
+ PowerLevel:
+ Skill_Mastery:
+ Enabled: true
+ AprilFoolsEvent: true
MOTD_Enabled: true
EventBroadcasts: true
EventInfoOnPlayerJoin: true
diff --git a/src/main/resources/custom_item_support.yml b/src/main/resources/custom_item_support.yml
new file mode 100644
index 000000000..76285867e
--- /dev/null
+++ b/src/main/resources/custom_item_support.yml
@@ -0,0 +1,11 @@
+# This is meant to be a general config for allowing mcMMO to allow interaction with custom items.
+# In the future, I would like to add configs to be specific about certain custom items.
+# For now, support is generalized to whether the custom item has a custom model.
+# This is an easy solution to implement for now, but not the most ideal.
+Custom_Item_Support:
+ Repair:
+ # Turn this off to disable repair on any items with custom model data
+ Allow_Repair_On_Items_With_Custom_Model_Data: true
+ Salvage:
+ # Turn this off to disable salvage on any items with custom model data
+ Allow_Salvage_On_Items_With_Custom_Model_Data: true
diff --git a/src/main/resources/experience.yml b/src/main/resources/experience.yml
index 00e72f01d..a41609521 100644
--- a/src/main/resources/experience.yml
+++ b/src/main/resources/experience.yml
@@ -39,6 +39,7 @@ ExploitFix:
TreeFellerReducedXP: true
PistonCheating: true
SnowGolemExcavation: true
+ PreventArmorStandInteraction: true
# This include NPCs from stuff like Citizens, this is not a setting for Vanilla Minecraft Villagers (Which can be considered NPCs)
# mcMMO normally doesn't process attacks against an Entity if it is an NPC from another plugin
# Of course, mcMMO doesn't know for sure whether something is an NPC, it checks a few known things, see our source code to see how
@@ -77,6 +78,10 @@ Experience_Bars:
Enable: true
Color: BLUE
BarStyle: SEGMENTED_6
+ Crossbows:
+ Enable: true
+ Color: BLUE
+ BarStyle: SEGMENTED_6
Excavation:
Enable: true
Color: YELLOW
@@ -113,6 +118,10 @@ Experience_Bars:
Enable: true
Color: RED
BarStyle: SEGMENTED_6
+ Tridents:
+ Enable: true
+ Color: BLUE
+ BarStyle: SEGMENTED_6
Unarmed:
Enable: true
Color: BLUE
@@ -159,8 +168,10 @@ Experience_Formula:
Breeding:
Multiplier: 1.0
- # Experience gained will get divided by these values. 1.0 by default, 2.0 means two times less XP gained.
- Modifier:
+ # Experience gained will get multiplied by these values. 1.0 by default, 0.5 means half XP gained. This happens right before multiplying the XP by the global multiplier.
+ Skill_Multiplier:
+ Crossbows: 1.0
+ Tridents: 1.0
Swords: 1.0
Taming: 1.0
Acrobatics: 1.0
diff --git a/src/main/resources/locale/locale_en_US.properties b/src/main/resources/locale/locale_en_US.properties
index 27c45d0d9..11e550b20 100644
--- a/src/main/resources/locale/locale_en_US.properties
+++ b/src/main/resources/locale/locale_en_US.properties
@@ -1,10 +1,5 @@
-#I'm going to try to normalize our locale file, forgive the mess for now.
-# TODO: Update JSON to support hex
-
-#DO NOT USE COLOR CODES IN THE JSON KEYS
-#COLORS ARE DEFINED IN advanced.yml IF YOU WISH TO CHANGE THEM
JSON.Rank=Rank
-JSON.DescriptionHeader=Description
+JSON.DescriptionHeader=Description:
JSON.JWrapper.Header=Details
JSON.Type.Passive=Passive
JSON.Type.Active=Active
@@ -21,6 +16,7 @@ JSON.Acrobatics=Acrobatics
JSON.Alchemy=Alchemy
JSON.Archery=Archery
JSON.Axes=Axes
+JSON.Crossbows=Crossbows
JSON.Excavation=Excavation
JSON.Fishing=Fishing
JSON.Herbalism=Herbalism
@@ -29,6 +25,7 @@ JSON.Repair=Repair
JSON.Salvage=Salvage
JSON.Swords=Swords
JSON.Taming=Taming
+JSON.Tridents=Tridents
JSON.Unarmed=Unarmed
JSON.Woodcutting=Woodcutting
JSON.URL.Website=The official mcMMO Website!
@@ -88,6 +85,7 @@ Overhaul.Name.Acrobatics=Acrobatics
Overhaul.Name.Alchemy=Alchemy
Overhaul.Name.Archery=Archery
Overhaul.Name.Axes=Axes
+Overhaul.Name.Crossbows=Crossbows
Overhaul.Name.Excavation=Excavation
Overhaul.Name.Fishing=Fishing
Overhaul.Name.Herbalism=Herbalism
@@ -97,6 +95,7 @@ Overhaul.Name.Salvage=Salvage
Overhaul.Name.Smelting=Smelting
Overhaul.Name.Swords=Swords
Overhaul.Name.Taming=Taming
+Overhaul.Name.Tridents=Tridents
Overhaul.Name.Unarmed=Unarmed
Overhaul.Name.Woodcutting=Woodcutting
# /mcMMO Command Style Stuff
@@ -112,6 +111,7 @@ XPBar.Acrobatics=Acrobatics Lv.&6{0}
XPBar.Alchemy=Alchemy Lv.&6{0}
XPBar.Archery=Archery Lv.&6{0}
XPBar.Axes=Axes Lv.&6{0}
+XPBar.Crossbows=Crossbows Lv.&6{0}
XPBar.Excavation=Excavation Lv.&6{0}
XPBar.Fishing=Fishing Lv.&6{0}
XPBar.Herbalism=Herbalism Lv.&6{0}
@@ -121,6 +121,7 @@ XPBar.Salvage=Salvage Lv.&6{0}
XPBar.Smelting=Smelting Lv.&6{0}
XPBar.Swords=Swords Lv.&6{0}
XPBar.Taming=Taming Lv.&6{0}
+XPBar.Tridents=Tridents Lv.&6{0}
XPBar.Unarmed=Unarmed Lv.&6{0}
XPBar.Woodcutting=Woodcutting Lv.&6{0}
#This is just a preset template that gets used if the 'ExtraDetails' setting is turned on in experience.yml (off by default), you can ignore this template and just edit the strings above
@@ -176,6 +177,13 @@ Archery.SubSkill.ArcheryLimitBreak.Description=Breaking your limits. Increased d
Archery.SubSkill.ArcheryLimitBreak.Stat=Limit Break Max DMG
Archery.Listener=Archery:
Archery.SkillName=ARCHERY
+Archery.SubSkill.ExplosiveShot.Name=Explosive Shot
+Archery.SubSkill.ExplosiveShot.Description=Fire an explosive arrow
+Archery.Skills.ExplosiveShot.Off=
+Archery.Skills.ExplosiveShot.On=&a**EXPLOSIVE SHOT ACTIVATED**
+Archery.Skills.ExplosiveShot.Other.Off=Explosive Shot&a has worn off for &e{0}
+Archery.Skills.ExplosiveShot.Other.On=&a{0}&2 has used &cExplosive Shot!
+Archery.Skills.ExplosiveShot.Refresh=&aYour &Explosive Shot &ability is refreshed!
#AXES
Axes.Ability.Bonus.0=Axe Mastery
Axes.Ability.Bonus.1=Bonus {0} damage
@@ -285,8 +293,11 @@ Herbalism.SubSkill.FarmersDiet.Name=Farmer's Diet
Herbalism.SubSkill.FarmersDiet.Description=Improves hunger restored from farmed foods
Herbalism.SubSkill.FarmersDiet.Stat=Farmer's Diet: &aRank {0}
Herbalism.SubSkill.DoubleDrops.Name=Double Drops
-Herbalism.SubSkill.DoubleDrops.Description=Double the normal loot
+Herbalism.SubSkill.DoubleDrops.Description=Skillfully harvest double the loot
Herbalism.SubSkill.DoubleDrops.Stat=Double Drop Chance
+Herbalism.SubSkill.VerdantBounty.Name=Verdant Bounty
+Herbalism.SubSkill.VerdantBounty.Description=Masterfully harvest triple the loot
+Herbalism.SubSkill.VerdantBounty.Stat=Triple Drop Chance
Herbalism.SubSkill.HylianLuck.Name=Hylian Luck
Herbalism.SubSkill.HylianLuck.Description=Gives a small chance of finding rare items
Herbalism.SubSkill.HylianLuck.Stat=Hylian Luck Chance
@@ -311,8 +322,11 @@ Mining.SubSkill.SuperBreaker.Name=Super Breaker
Mining.SubSkill.SuperBreaker.Description=Speed+, Triple Drop Chance
Mining.SubSkill.SuperBreaker.Stat=Super Breaker Length
Mining.SubSkill.DoubleDrops.Name=Double Drops
-Mining.SubSkill.DoubleDrops.Description=Double the normal loot
+Mining.SubSkill.DoubleDrops.Description=Skillfully mine double the loot
Mining.SubSkill.DoubleDrops.Stat=Double Drop Chance
+Mining.SubSkill.MotherLode.Name=Mother Lode
+Mining.SubSkill.MotherLode.Description=Masterfully mine triple the loot
+Mining.SubSkill.MotherLode.Stat=Triple Drop Chance
Mining.SubSkill.BlastMining.Name=Blast Mining
Mining.SubSkill.BlastMining.Description=Bonuses to mining with TNT
Mining.SubSkill.BlastMining.Stat=Blast Mining:&a Rank {0}/{1} &7({2})
@@ -395,7 +409,7 @@ Salvage.Skills.Adept.Level=You must be level &e{0}&c to salvage &e{1}
Salvage.Skills.TooDamaged=&4This item is too damaged to be salvaged.
Salvage.Skills.ArcaneFailed=&cYou were unable to extract the knowledge contained within this item.
Salvage.Skills.ArcanePartial=&cYou were only able to extract some of the knowledge contained within this item.
-Salvage.Skills.ArcaneSuccess=&aYou able to extract all of the knowledge contained within this item!
+Salvage.Skills.ArcaneSuccess=&aYou were able to extract all the knowledge contained within this item!
Salvage.Listener.Anvil=&4You have placed a Salvage anvil, use this to Salvage tools and armor.
Salvage.Listener=Salvage:
Salvage.SkillName=SALVAGE
@@ -404,6 +418,53 @@ Salvage.Skills.Lottery.Perfect=&a&lPerfect!&r&6 You salvaged &3{1}&6 effortlessl
Salvage.Skills.Lottery.Untrained=&7You aren't properly trained in salvaging. You were only able to recover &c{0}&7 materials from &a{1}&7.
#Anvil (Shared between SALVAGE and REPAIR)
Anvil.Unbreakable=This item is unbreakable!
+Anvil.Repair.Reject.CustomModelData=A mysterious force prevents you from repairing this item...
+Anvil.Salvage.Reject.CustomModelData=A mysterious force prevents you from salvaging this item...
+#CROSSBOWS
+Crossbows.SkillName=CROSSBOWS
+Crossbows.Ability.Lower=&7You lower your crossbow.
+Crossbows.Ability.Ready=&3You &6ready&3 your Crossbow.
+Crossbows.Skills.SSG.Refresh=&aYour &eSuper Shotgun &aability is refreshed!
+Crossbows.Skills.SSG.Other.On=&a{0}&2 used &Super Shotgun!
+Crossbows.SubSkill.PoweredShot.Name=Powered Shot
+Crossbows.SubSkill.PoweredShot.Description=Increases damage done with crossbows
+Crossbows.SubSkill.PoweredShot.Stat=Powered Shot Bonus Damage
+Crossbows.SubSkill.CrossbowsLimitBreak.Name=Crossbows Limit Break
+Crossbows.SubSkill.CrossbowsLimitBreak.Description=Breaking your limits. Increased damage against tough opponents. Intended for PVP, up to server settings for whether it will boost damage in PVE.
+Crossbows.SubSkill.CrossbowsLimitBreak.Stat=Limit Break Max DMG
+Crossbows.SubSkill.TrickShot.Name=Trick Shot
+Crossbows.SubSkill.TrickShot.Description=Richochet arrows with steep angles
+Crossbows.SubSkill.TrickShot.Stat=Trick Shot Max Bounces
+Crossbows.SubSkill.TrickShot.Stat.Extra=Trick Shot Max Bounces: &a{0}
+Crossbows.SubSkill.TrickShot.Stat.Extra2=Trick Shot Reduced DMG per Bounce: &a{0}
+Crossbows.SubSkill.SuperShotgun.Name=Super Shotgun
+Crossbows.Listener=Crossbows:
+
+#TRIDENTS
+Tridents.SkillName=TRIDENTS
+Tridents.Ability.Lower=&7You lower your trident.
+Tridents.Ability.Ready=&3You &6ready&3 your Trident.
+Tridents.SubSkill.Impale.Name=Impale
+Tridents.SubSkill.Impale.Description=Increases damage done with tridents
+Tridents.SubSkill.Impale.Stat=Impale Bonus Damage
+Tridents.SubSkill.TridentsLimitBreak.Name=Tridents Limit Break
+Tridents.SubSkill.TridentsLimitBreak.Description=Breaking your limits. Increased damage against tough opponents. Intended for PVP, up to server settings for whether it will boost damage in PVE.
+Tridents.SubSkill.TridentsLimitBreak.Stat=Limit Break Max DMG
+Tridents.SubSkill.TridentAbility.Name=WIP
+Tridents.Listener=Tridents:
+
+#MACES
+Maces.SkillName=MACES
+Maces.Ability.Lower=&7You lower your mace.
+Maces.Ability.Ready=&3You &6ready&3 your Mace.
+Maces.Skills.MaceSmash.Refresh=&aYour &eGiga Smash &aability is refreshed!
+Maces.Skills.MaceSmash.Other.On=&a{0}&2 used &cGiga Smash!
+Maces.SubSkill.GigaSmash.Name=Giga Smash
+Maces.SubSkill.MacesLimitBreak.Name=Maces Limit Break
+Maces.SubSkill.MacesLimitBreak.Description=Breaking your limits. Increased damage against tough opponents. Intended for PVP, up to server settings for whether it will boost damage in PVE.
+Maces.SubSkill.MacesLimitBreak.Stat=Limit Break Max DMG
+Maces.Listener=Maces:
+
#SWORDS
Swords.Ability.Lower=&7You lower your sword.
Swords.Ability.Ready=&3You &6ready&3 your Sword.
@@ -542,8 +603,11 @@ Woodcutting.SubSkill.KnockOnWood.Stat=Knock on Wood
Woodcutting.SubSkill.KnockOnWood.Loot.Normal=Standard loot from trees
Woodcutting.SubSkill.KnockOnWood.Loot.Rank2=Standard loot from trees and experience orbs
Woodcutting.SubSkill.HarvestLumber.Name=Harvest Lumber
-Woodcutting.SubSkill.HarvestLumber.Description=Skillfully extract more Lumber
+Woodcutting.SubSkill.HarvestLumber.Description=Skillfully extract up to double the Lumber
Woodcutting.SubSkill.HarvestLumber.Stat=Double Drop Chance
+Woodcutting.SubSkill.CleanCuts.Name=Clean Cuts
+Woodcutting.SubSkill.CleanCuts.Description=Masterfully extract up to triple the Lumber
+Woodcutting.SubSkill.CleanCuts.Stat=Triple Drop Chance
Woodcutting.SubSkill.Splinter.Name=Splinter
Woodcutting.SubSkill.Splinter.Description=Cut down trees more efficiently.
Woodcutting.SubSkill.BarkSurgeon.Name=Bark Surgeon
@@ -715,7 +779,7 @@ Commands.XPBar.Reset=&6XP Bar settings for mcMMO have been reset.
Commands.XPBar.SettingChanged=&6XP Bar setting for &a{0}&6 is now set to &a{1}
Commands.Skill.Invalid=That is not a valid skillname!
Commands.Skill.ChildSkill=Child skills are not valid for this command!
-Commands.Skill.Leaderboard=--mcMMO &9{0}&e Leaderboard--
+Commands.Skill.Leaderboard=-&e-mcMMO &9{0}&e Leaderboard--
Commands.SkillInfo=&a- View detailed information about a skill
Commands.Stats=&a- View your mcMMO stats
Commands.ToggleAbility=&a- Toggle ability activation with right click
@@ -830,6 +894,7 @@ Commands.XPGain.Alchemy=Brewing Potions
Commands.XPGain.Archery=Attacking Monsters
Commands.XPGain.Axes=Attacking Monsters
Commands.XPGain.Child=Gains levels from Parent Skills
+Commands.XPGain.Crossbows=Attacking Monsters
Commands.XPGain.Excavation=Digging and finding treasures
Commands.XPGain.Fishing=Fishing (Go figure!)
Commands.XPGain.Herbalism=Harvesting Herbs
@@ -837,6 +902,7 @@ Commands.XPGain.Mining=Mining Stone & Ore
Commands.XPGain.Repair=Repairing
Commands.XPGain.Swords=Attacking Monsters
Commands.XPGain.Taming=Animal Taming, or combat w/ your wolves
+Commands.XPGain.Tridents=Attacking Monsters
Commands.XPGain.Unarmed=Attacking Monsters
Commands.XPGain.Woodcutting=Chopping down trees
Commands.XPGain=&8XP GAIN: &f{0}
@@ -970,6 +1036,13 @@ Guides.Woodcutting.Section.0=&3About Woodcutting:\n&eWoodcutting is all about ch
Guides.Woodcutting.Section.1=&3How does Tree Feller work?\n&eTree Feller is an active ability, you can right-click\n&ewhile holding an ax to activate Tree Feller. This will\n&ecause the entire tree to break instantly, dropping all\n&eof its logs at once.
Guides.Woodcutting.Section.2=&3How does Leaf Blower work?\n&eLeaf Blower is a passive ability that will cause leaf\n&eblocks to break instantly when hit with an axe. By default,\nðis ability unlocks at level 100.
Guides.Woodcutting.Section.3=&3How do Double Drops work?\n&eThis passive ability gives you a chance to obtain an extra\n&eblock for every log you chop.
+# Crossbows
+Guides.Crossbows.Section.0=&3About Crossbows:\n&eCrossbows is all about shooting with your crossbow.\n\n&3XP GAIN:\n&eXP is gained whenever you shoot mobs with a crossbow.
+Guides.Crossbows.Section.1=&3How does Trickshot work?\n&eTrickshot is an passive ability, you shoot your bolts at a shallow angle with a crossbow to attempt a Trickshot. This will cause the arrow to ricochet off of blocks and potentially hit a target. The number of potential bounces from a ricochet depend on the rank of Trickshot.
+# Tridents
+Guides.Tridents.Section.0=&3About Tridents:\n&eTridents skill involves impaling foes with your trident.\n\n&3XP GAIN:\n&eXP is gained whenever you hit mobs with a trident.
+
+
#INSPECT
Inspect.Offline= &cYou do not have permission to inspect offline players!
Inspect.OfflineStats=mcMMO Stats for Offline Player &e{0}
diff --git a/src/main/resources/locale/locale_ko.properties b/src/main/resources/locale/locale_ko.properties
index 5eabe51e2..4f1533bd6 100644
--- a/src/main/resources/locale/locale_ko.properties
+++ b/src/main/resources/locale/locale_ko.properties
@@ -1,60 +1,49 @@
# korean locale file v2.0
# -----------------
-# Setting up a new standard for string names, for easier localization.
-# SkillName.SubType.LocalEffect.Increment
-# Skill name, such as ACROBATICS, MINING.
-# Sub-type, such as EFFECT, ABILITY, COMBAT.
-# Local Effect, local text for Skill Name; ACROBATICS_ROLL, IGNITIONTIME,GRACECHANCE.
-# Increment, for multi-line or array-like lists.
-# ######
-#
-# EXAMPLES:
-#
-# Acrobatics.Ability.Proc
-# Axes.Ability.Refresh.1
-#
-# --wolfwork
+# Work : MangChi__ / JJangGu
+#로케일 파일을 정규화하려고 합니다. 지금은 혼란스러운 점을 용서해 주세요.
+# TODO: 16진수를 지원하도록 JSON을 업데이트합니다.
-#DO NOT USE COLOR CODES IN THE JSON KEYS
-#COLORS ARE DEFINED IN advanced.yml IF YOU WISH TO CHANGE THEM
-JSON.Rank=랭크
+#JSON 키에 색상 코드를 사용하지 마세요.
+#색상을 변경하려는 경우에는 Advanced.yml에 정의되어 있습니다.
+JSON.Rank=등급
JSON.DescriptionHeader=설명
JSON.JWrapper.Header=세부 정보
-JSON.Type.Passive=패시브
-JSON.Type.Active=액티브
+JSON.Type.Passive=수동적
+JSON.Type.Active=능동적
JSON.Type.SuperAbility=슈퍼 능력
-JSON.Locked=-=[해금되지 않음]=-
+JSON.Locked=-=[잠김]=-
JSON.LevelRequirement=레벨 요구 사항
JSON.JWrapper.Target.Type=대상 유형:
JSON.JWrapper.Target.Block=블록
JSON.JWrapper.Target.Player=플레이어
-JSON.JWrapper.Perks.Header=&6행운 퍽
+JSON.JWrapper.Perks.Header=&6운이 좋은 특성
JSON.JWrapper.Perks.Lucky={0}% 더 좋은 확률
JSON.Hover.Tips=팁
JSON.Acrobatics=곡예
JSON.Alchemy=연금술
-JSON.Archery=궁술
-JSON.Axes=부술
-JSON.Excavation=발굴
+JSON.Archery=활쏘기
+JSON.Axes=참수
+JSON.Excavation=파괴
JSON.Fishing=낚시
-JSON.Herbalism=약초학
+JSON.Herbalism=약초 채집
JSON.Mining=채광
JSON.Repair=수리
-JSON.Salvage=회수
+JSON.Salvage=분해
JSON.Swords=검술
-JSON.Taming=조련
-JSON.Unarmed=비무장
-JSON.Woodcutting=벌목
-JSON.URL.Website=mcMMO 공식 웹사이트!
-JSON.URL.Discord=mcMMO 공식 디스코드 서버!
-JSON.URL.Patreon=nossr50와 mcMMO를 위한 Patreon에서 지원하세요!
-JSON.URL.Spigot=mcMMO 공식 Spigot 리소스 페이지!
+JSON.Taming=길들이기
+JSON.Unarmed=맨손전투
+JSON.Woodcutting=나무 베기
+JSON.URL.Website=공식 mcMMO 웹사이트!
+JSON.URL.Discord=공식 mcMMO 디스코드 서버!
+JSON.URL.Patreon=nossr50과 mcMMO에 대한 작업을 Patreon에서 지원하세요!
+JSON.URL.Spigot=공식 mcMMO Spigot 리소스 페이지!
JSON.URL.Translation=mcMMO를 다른 언어로 번역하세요!
-JSON.URL.Wiki=mcMMO 공식 위키!
-JSON.SkillUnlockMessage=&6[ mcMMO&e @&3{0} &6랭크 &3{1}&6이(가) 해제되었습니다! ]
-JSON.Hover.Rank=&e&l랭크:&r &f{0}
+JSON.URL.Wiki=공식 mcMMO 위키!
+JSON.SkillUnlockMessage=&6[ mcMMO&e @&3{0} &6등급 &3{1}&6 해제되었습니다! ]
+JSON.Hover.Rank=&e&l등급:&r &f{0}
JSON.Hover.NextRank=&7&o다음 업그레이드: 레벨 {0}
-# for JSON.Hover.Mystery you can add {0} to insert the level required into the name, I don't like how that looks so I'm not doing that atm
+# JSON.Hover.Mystery에 {0}을 추가하여 필요한 레벨을 이름에 삽입할 수 있지만, 현재는 사용하지 않습니다.
JSON.Hover.Mystery=&7???
JSON.Hover.Mystery2=&e[&8{0}&e]&8???&r
JSON.Hover.SkillName=&3{0}&r
@@ -63,29 +52,27 @@ JSON.Hover.MaxRankSkillName=&6{0}&r
JSON.Hover.AtSymbolSkills=&e@
JSON.Hover.AtSymbolURL=&e@
-#This is the message sent to players when an ability is activated
+# 능력이 활성화될 때 플레이어에게 전송되는 메시지입니다.
JSON.Notification.SuperAbility={0}
-#These are the JSON Strings used for SubSkills
-JSON.Acrobatics.Roll.Interaction.Activated=테스트 &c구른 테스트
-JSON.Acrobatics.SubSkill.Roll.Details.Tips=낙하 중 웅크리기 키를 누르면 일반적으로 받을 데미지의 2배까지 방지할 수 있습니다!
-Anvil.SingleItemStack=&c하나 이상의 아이템을 가진 아이템 스택은 회수하거나 수리할 수 없습니다. 먼저 스택을 분할하세요.
+# 하위 스킬에 사용되는 JSON 문자열입니다.
+JSON.Acrobatics.Roll.Interaction.Activated=테스트 &c굴러가기 테스트
+JSON.Acrobatics.SubSkill.Roll.Details.Tips=낙하 중에 스프린트 키를 누르면 일반적으로 받을 데미지의 최대 2배까지 예방할 수 있습니다!
+Anvil.SingleItemStack=&c한 개 이상의 아이템이 있는 아이템 스택은 분해하거나 수리할 수 없습니다. 먼저 스택을 나누세요.
-#DO NOT USE COLOR CODES IN THE JSON KEYS
-#COLORS ARE DEFINED IN advanced.yml IF YOU WISH TO CHANGE THEM
+#JSON 키에 색상 코드를 사용하지 마세요.
+#색상을 변경하려는 경우에는 Advanced.yml에 정의되어 있습니다.
mcMMO.Template.Prefix=&6(&amcMMO&6) &7{0}
-
-# BEGIN STYLING
-Ability.Generic.Refresh=&a**능력이 갱신되었습니다!**
+# 스타일링 시작
+Ability.Generic.Refresh=&a**능력 갱신됨!**
Ability.Generic.Template.Lock=&7{0}
-
-# Skill Command Styling
+# 스킬 명령어 스타일링
Ability.Generic.Template=&3{0}: &a{1}
Ability.Generic.Template.Custom=&3{0}
Skills.Overhaul.Header=&c[]=====[]&a {0} &c[]=====[]
Effects.Effects=효과
-Effects.SubSkills.Overhaul=부가 스킬
+Effects.SubSkills.Overhaul=하위 스킬
Effects.Child.Overhaul=&3하위 레벨.&e {0}&3: {1}
Effects.Child.ParentList=&a{0}&6(&3레벨.&e{1}&6)
Effects.Level.Overhaul=&6레벨: &e{0} &3경험치&e(&6{1}&e/&6{2}&e)
@@ -93,1197 +80,1077 @@ Effects.Parent=&6{0} -
Effects.Template=&3{0}: &a{1}
Commands.Stats.Self.Overhaul=스탯
Commands.XPGain.Overhaul=&6경험치 획득: &3{0}
-MOTD.Version.Overhaul=&6[mcMMO] &3Overhaul 시대&6 - &3{0}
-Overhaul.mcMMO.Header=&c[]=====[]&a mcMMO - Overhaul 시대 &c[]=====[]
+MOTD.Version.Overhaul=&6[mcMMO] &3오버홀 시대&6 - &3{0}
+Overhaul.mcMMO.Header=&c[]=====[]&a mcMMO - 오버홀 시대 &c[]=====[]
Overhaul.mcMMO.Url.Wrap.Prefix=&c[|
Overhaul.mcMMO.Url.Wrap.Suffix=&c|]
-Overhaul.mcMMO.MmoInfo.Wiki=&e[&f이 스킬을 위키에서 보기!&e]
-
+Overhaul.mcMMO.MmoInfo.Wiki=&e[&f이 스킬을 위키에서 확인하세요!&e]
+# Overhaul.Levelup can take {0} - Skill Name defined in Overhaul.Name {1} - Amount of levels gained {2} - Level in skill
+Overhaul.Levelup=&l{0} 능력이 증가하여 &r&a&l{2}&r&f이 되었습니다.
+Overhaul.Name.Acrobatics=곡예
+Overhaul.Name.Alchemy=연금술
+Overhaul.Name.Archery=활쏘기
+Overhaul.Name.Axes=참수
+Overhaul.Name.Excavation=파괴
+Overhaul.Name.Fishing=낚시
+Overhaul.Name.Herbalism=약초 채집
+Overhaul.Name.Mining=채광
+Overhaul.Name.Repair=수리
+Overhaul.Name.Salvage=분해
+Overhaul.Name.Smelting=제련
+Overhaul.Name.Swords=검술
+Overhaul.Name.Taming=길들이기
+Overhaul.Name.Unarmed=맨손 전투
+Overhaul.Name.Woodcutting=벌목
# /mcMMO Command Style Stuff
Commands.mcc.Header=&c---[]&amcMMO 명령어&c[]---
Commands.Other=&c---[]&a특수 명령어&c[]---
Commands.Party.Header=&c-----[]&a파티&c[]-----
Commands.Party.Features.Header=&c-----[]&a기능&c[]-----
-# XP BAR Allows for the following variables -- {0} = Skill Level, {1} Current XP, {2} XP Needed for next level, {3} Power Level, {4} Percentage of Level
-# Make sure you turn on Experience_Bars.ThisMayCauseLag.AlwaysUpdateTitlesWhenXPIsGained if you want the XP bar title to update every time a player gains XP!
+# XP 바는 다음 변수를 사용할 수 있습니다. -- {0} = 스킬 레벨, {1} 현재 경험치, {2} 다음 레벨까지 필요한 경험치, {3} 파워 레벨, {4} 레벨의 백분율
+# XP 바 제목이 플레이어가 경험치를 획득할 때마다 업데이트되기를 원한다면 Experience_Bars.ThisMayCauseLag.AlwaysUpdateTitlesWhenXPIsGained를 켜주세요!
XPBar.Template={0}
-XPBar.Template.EarlyGameBoost=&6새로운 스킬을 습득하는 중...
-XPBar.Acrobatics=곡예 Lv.&6{0}
-XPBar.Alchemy=연금술 Lv.&6{0}
-XPBar.Archery=궁술 Lv.&6{0}
-XPBar.Axes=부술 Lv.&6{0}
-XPBar.Excavation=발굴 Lv.&6{0}
-XPBar.Fishing=낚시 Lv.&6{0}
-XPBar.Herbalism=약초학 Lv.&6{0}
-XPBar.Mining=채광 Lv.&6{0}
-XPBar.Repair=수리 Lv.&6{0}
-XPBar.Salvage=회수 Lv.&6{0}
-XPBar.Smelting=제련 Lv.&6{0}
-XPBar.Swords=검술 Lv.&6{0}
-XPBar.Taming=조련 Lv.&6{0}
-XPBar.Unarmed=비무장 Lv.&6{0}
-XPBar.Woodcutting=벌목 Lv.&6{0}
+XPBar.Template.EarlyGameBoost=&6새로운 스킬 배움 중...
+XPBar.Acrobatics=곡예 레벨 &6{0}
+XPBar.Alchemy=연금술 레벨 &6{0}
+XPBar.Archery=활쏘기 레벨 &6{0}
+XPBar.Axes=참수 레벨 &6{0}
+XPBar.Excavation=파괴 레벨 &6{0}
+XPBar.Fishing=낚시 레벨 &6{0}
+XPBar.Herbalism=약초 채집 레벨 &6{0}
+XPBar.Mining=채광 레벨 &6{0}
+XPBar.Repair=수리 레벨 &6{0}
+XPBar.Salvage=분해 레벨 &6{0}
+XPBar.Smelting=제련 레벨 &6{0}
+XPBar.Swords=검술 레벨 &6{0}
+XPBar.Taming=길들이기 레벨 &6{0}
+XPBar.Unarmed=맨손 전투 레벨 &6{0}
+XPBar.Woodcutting=벌목 레벨 &6{0}
+#이것은 experience.yml에서 'ExtraDetails' 설정이 켜져 있는 경우(기본적으로 꺼져 있음) 사용되는 미리 설정된 템플릿일 뿐입니다. 이 템플릿을 무시하고 위의 문자열만 편집하면 됩니다.
XPBar.Complex.Template={0} &3 {4}&f% &3(&f{1}&3/&f{2}&3)
-#This is just a preset template that gets used if the 'ExtraDetails' setting is turned on in experience.yml (off by default), you can ignore this template and just edit the strings above
-XPBar.Complex.Template={0} &3 {4}&f% &3(&f{1}&3/&f{2}&3)
-# XP BAR Allows for the following variables -- {0} = Skill Level, {1} Current XP, {2} XP Needed for next level, {3} Power Level, {4} Percentage of Level
-# Make sure you turn on Experience_Bars.ThisMayCauseLag.AlwaysUpdateTitlesWhenXPIsGained if you want the XP bar title to update every time a player gains XP!
-# END STYLING
+# XP 바는 다음 변수를 사용할 수 있습니다. -- {0} = 스킬 레벨, {1} 현재 경험치, {2} 다음 레벨까지 필요한 경험치, {3} 파워 레벨, {4} 레벨의 백분율
+# XP 바 제목이 플레이어가 경험치를 획득할 때마다 업데이트되기를 원한다면 Experience_Bars.ThisMayCauseLag.AlwaysUpdateTitlesWhenXPIsGained를 켜주세요!
+# 스타일링 끝
-
-#ACROBATICS
-Acrobatics.SubSkill.Roll.Stats=&6구르기 확률 &e{0}%&6 우아한 구르기 확률&e {1}%
-Acrobatics.SubSkill.Roll.Stat=구르기 확률
-Acrobatics.SubSkill.Roll.Stat.Extra=우아한 구르기 확률
-Acrobatics.SubSkill.Roll.Chance=구르기 확률: &e{0}
-Acrobatics.SubSkill.Roll.Mechanics=&7구르기는 수동적인 부분이 있는 능력입니다.\n낙하 데미지를 입을 때마다, 당신은 당신의 능력 레벨에 따라 데미지를 완전히 무효화할 수 있습니다. 능력 레벨이 &e{6}%&7일 때, 당신은 &e{0}%&7의 확률로 데미지를 무효화할 수 있으며, 우아한 구르기를 활성화하면 &e{1}%&7의 확률로 데미지를 무효화할 수 있습니다.\n성공 확률은 능력 레벨에 따라 선형적으로 조정되며, 능력 레벨 &e{2}&7에서 최대치에 도달합니다. 곡예 능력을 1 레벨 올릴 때마다 &e{3}%&7의 확률이 증가합니다.\n웅크리기 버튼을 누르면 구르기 확률이 2배가 되며, 데미지를 2배로 감소시킬 수 있습니다. 웅크리기를 누르면 일반적인 구르기가 우아한 구르기로 변합니다.\n구르기는 최대 &c{4}&7의 데미지를 무효화합니다. 우아한 구르기는 최대 &a{5}&7의 데미지를 무효화합니다.
-Acrobatics.SubSkill.GracefulRoll.Name=우아한 구르기
-Acrobatics.SubSkill.Dodge.Stat=구르기 확률
-Acrobatics.Ability.Proc=&a**우아한 구르기**
-Acrobatics.Combat.Proc=&a**회피**
-Acrobatics.DodgeChance=회피 확률: &e{0}
-Acrobatics.SubSkill.Roll.Name=구르기
-Acrobatics.SubSkill.Roll.Description=추락 데미지 감소 또는 무효
-Acrobatics.SubSkill.GracefulRoll.Name=우아한 구르기
-Acrobatics.SubSkill.GracefulRoll.Description=구르기 2배 효과
+#재주 넘기
+Acrobatics.Ability.Proc=&a**용아치** (Graceful Landing)
+Acrobatics.Combat.Proc=&a**회피** (Dodged)
+Acrobatics.SubSkill.Roll.Stats=&6Roll 확률 &e{0}%&6 우아한 Roll 확률 &e{1}%
+Acrobatics.SubSkill.Roll.Stat=Roll 확률
+Acrobatics.SubSkill.Roll.Stat.Extra=우아한 Roll 확률
+Acrobatics.SubSkill.Roll.Name=Roll
+Acrobatics.SubSkill.Roll.Description=전략적으로 착지하여 피해를 피합니다.
+Acrobatics.SubSkill.Roll.Chance=Roll 확률: &e{0}
+Acrobatics.SubSkill.Roll.GraceChance=우아한 Roll 확률: &e{0}
+Acrobatics.SubSkill.Roll.Mechanics=&7Roll은 수동 구성 요소가 있는 능동적인 하위 기술입니다.\n낙하 피해를 입을 때마다 기술 레벨에 따라 피해를 완전히 무효화할 기회가 있습니다. 레벨 &e{6}%&7에서 피해를 방지할 확률은 &e{0}%&7이며, 우아한 Roll을 활성화하면 &e{1}%&7입니다.\n성공 확률은 능력 레벨에 따라 선형 곡선으로 조정되며, 능력이 있는 Acrobatics 레벨마다 &e{3}%&7의 성공 확률이 주어집니다.\n스니크 버튼을 누르면 낙하 피해를 피하는 기회가 두 배로 늘어나며, 최대 2배의 낙하 피해를 피할 수 있습니다! 스니크를 누르면 일반적인 Roll이 우아한 Roll로 변합니다.\nRoll은 최대 &c{4}&7의 피해를 방지할 수 있으며, 우아한 Roll은 최대 &a{5}&7의 피해를 방지할 수 있습니다.
+Acrobatics.SubSkill.GracefulRoll.Name=우아한 Roll
+Acrobatics.SubSkill.GracefulRoll.Description=일반 Roll의 2배 효과적인 기술
Acrobatics.SubSkill.Dodge.Name=회피
-Acrobatics.SubSkill.Dodge.Description=낙하 데미지 절반 감소
-Acrobatics.Listener=곡예(ACROBATICS):
-Acrobatics.SubSkill.Roll.Chance=구르기 확률: &e{0}
-Acrobatics.SubSkill.Roll.GraceChance=우아한 구르기 확률: &e{0}
-Acrobatics.Roll.Text=**구르기**
+Acrobatics.SubSkill.Dodge.Description=공격 피해를 절반으로 줄입니다.
+Acrobatics.SubSkill.Dodge.Stat=회피 확률
+Acrobatics.Listener=곡예:
+Acrobatics.Roll.Text=&o**Roll 사용**
Acrobatics.SkillName=곡예
-Acrobatics.Skillup=낙법 기술이 {0} 올라 총 {1} 레벨이 되었습니다
-#ALCHEMY
-Alchemy.SubSkill.Catalysis.Name=촉매
-Alchemy.SubSkill.Catalysis.Description=포션 양조 속도 증가
-Alchemy.SubSkill.Concoctions.Name=혼합
-Alchemy.SubSkill.Concoctions.Description=더 많은 성분의 포션 양조
-Alchemy.Listener=연금술(ALCHEMY):
-Alchemy.Ability.Locked.0={0}레벨 때 기술해제 (촉매)
-Alchemy.Catalysis.Speed=양조 속도: &e{0}
-Alchemy.Concoctions.Rank=혼합 랭크: &e{0}/{1}
-Alchemy.Concoctions.Ingredients=성분 [&e{0}&c]: &e{1}
-Alchemy.SkillName=연금술
-Alchemy.Skillup=연금술 기술이 {0} 올라 총 {1} 레벨이 되었습니다
-Alchemy.SubSkill.Catalysis.Stat=양조 속도
-Alchemy.SubSkill.Concoctions.Stat=혼합 랭크: &a{0}&3/&a{1}
-Alchemy.SubSkill.Concoctions.Stat.Extra=성분 [&a{0}&3]: &a{1}
-Alchemy.Ability.Locked.0={0}+ 레벨에서 해금됩니다 (촉매)
+#연금술
+Alchemy.SubSkill.Catalysis.Name=촉매작용
+Alchemy.SubSkill.Catalysis.Description=포션 제조 속도를 증가시킵니다.
+Alchemy.SubSkill.Catalysis.Stat=포션 제조 속도
+Alchemy.SubSkill.Concoctions.Name=조합
+Alchemy.SubSkill.Concoctions.Description=더 많은 재료로 포션을 제조합니다.
+Alchemy.SubSkill.Concoctions.Stat=조합 순위: &a{0}&3/&a{1}
+Alchemy.SubSkill.Concoctions.Stat.Extra=재료 [&a{0}&3]: &a{1}
+Alchemy.Listener=연금술:
+Alchemy.Ability.Locked.0={0}+ SKILL (CATALYSIS)까지 잠금
Alchemy.SkillName=연금술
-#ARCHERY
-Archery.Combat.DazeChance=현혹 확률: &e{0}
-Archery.Combat.RetrieveChance=화살 회수 확률: &e{0}
-Archery.Combat.SkillshotBonus=쏘기 솜씨 추가 피해 확률: &e{0}
-Archery.SubSkill.SkillShot.Name=쏘기 솜씨
-Archery.SubSkill.SkillShot.Description=활 피해 영구 증가
-Archery.SubSkill.Daze.Name=현혹 (플레이어)
-Archery.SubSkill.Daze.Description=적에게 혼란, {0} 피해 추가
+#양궁
+Archery.SubSkill.SkillShot.Name=스킬 사격
+Archery.SubSkill.SkillShot.Description=활로 입히는 피해량을 증가시킵니다.
+Archery.SubSkill.SkillShot.Stat=스킬 사격 추가 피해
+Archery.SubSkill.Daze.Name=혼란시키기
+Archery.SubSkill.Daze.Description=상대를 혼란시키고 추가 피해를 입힙니다.
+Archery.SubSkill.Daze.Stat=혼란 확률
Archery.SubSkill.ArrowRetrieval.Name=화살 회수
-Archery.SubSkill.ArrowRetrieval.Description=시체에서 화살 회수 확률 증가
-Archery.Listener=궁술(ARCHERY):
-Archery.SkillName=궁술
-Archery.Skillup=궁술 기술이 {0} 올라 총 {1} 레벨이 되었습니다
-Archery.SubSkill.SkillShot.Stat=스킬샷 추가 피해
-Archery.SubSkill.Daze.Stat=현혹 확률
+Archery.SubSkill.ArrowRetrieval.Description=시체에서 화살을 회수할 확률이 있습니다.
Archery.SubSkill.ArrowRetrieval.Stat=화살 회수 확률
-Archery.SubSkill.ArcheryLimitBreak.Name=궁술 한계 돌파
-Archery.SubSkill.ArcheryLimitBreak.Description=한계 돌파. 강한 적에게 추가 피해를 줍니다. PVP를 위해 만들어졌으며, PVE에서 추가 피해를 줄지는 서버 설정에 따라 다릅니다.
-Archery.SubSkill.ArcheryLimitBreak.Stat=한계 돌파 최대 피해
+Archery.SubSkill.ArcheryLimitBreak.Name=양궁 한계 극복
+Archery.SubSkill.ArcheryLimitBreak.Description=한계를 극복합니다. 강력한 상대에 대한 피해량이 증가합니다. PVP용으로 적용되며, PVE에서 피해량을 증가시킬지는 서버 설정에 따릅니다.
+Archery.SubSkill.ArcheryLimitBreak.Stat=한계 극복 최대 피해량
+Archery.Listener=양궁:
+Archery.SkillName=양궁
-
-#AXES
-Axes.Ability.Bonus.0=도끼 마스터리
-Axes.Ability.Bonus.1={0} 추가 피해
-Axes.Ability.Bonus.2=갑옷 충격
-Axes.Ability.Bonus.3=방어구 추가 피해: {0}
-Axes.Ability.Bonus.4=엄청난 충격
-Axes.Ability.Bonus.5=비무장 추가 피해: {0}
-Axes.Ability.Lower=&7**도끼 준비 해제**
-Axes.Ability.Ready=&a**도끼 준비 완료**
-Axes.Combat.CritStruck=&4크리티컬 히트에 맞았습니다!
-Axes.Combat.CritChance=크리티컬 히트 확률: &e{0}
-Axes.Combat.CriticalHit=크리티컬 히트!
-Axes.Combat.GI.Proc=&a**최고의 강타를 때렸습니다**
-Axes.Combat.GI.Struck=**엄청난 충격을 받았습니다**
-Axes.Combat.SS.Struck=&4뼈 쪼개기에 맞았습니다!
-Axes.Combat.SS.Length=뼈 쪼개기 지속시간: &e{0}초
-Axes.SubSkill.SkullSplitter.Name=뼈 쪼개기 (능력)
-Axes.SubSkill.SkullSplitter.Description=광역 추가 피해
-Axes.SubSkill.CriticalStrikes.Name=크리티컬 스트라이크
-Axes.SubSkill.CriticalStrikes.Description=피해 2배
-Axes.SubSkill.AxeMastery.Name=도끼 마스터리
-Axes.SubSkill.AxeMastery.Description=추가 특혜 피해
-Axes.SubSkill.ArmorImpact.Name=갑옷 충격
-Axes.SubSkill.ArmorImpact.Description=갑옷 파괴 공격
-Axes.SubSkill.GreaterImpact.Name=엄청난 충격
-Axes.SubSkill.GreaterImpact.Description=비무장 추가 피해
-Axes.Listener=부술(AXES):
-Axes.SkillName=부술
-Axes.Skills.SS.Off=**뼈 쪼개기 발동 해제**
-Axes.Skills.SS.On=&a**뼈 쪼개기 발동**
-Axes.Skills.SS.Refresh=&a당신의 &e뼈 쪼개기 &a기술은 이제 사용 가능합니다!
-Axes.Skills.SS.Other.Off={0}님이 &c뼈 쪼개기를&a 준비 해제했습니다
-Axes.Skills.SS.Other.On=&a{0}&2님이 &c뼈 쪼개기를 사용했습니다!
-Axes.Skillup=부술 기술이 {0} 올라 총 ({1}) 레벨이 되었습니다
-Axes.Ability.Ready.Extra=도끼 준비 완료. &7({0}은 {1}초 동안 쿨타임 중입니다)
-Axes.SubSkill.SkullSplitter.Stat=뼈 쪼개기 지속시간
-Axes.SubSkill.CriticalStrikes.Stat=크리티컬 스트라이크 확률
-Axes.SubSkill.AxesLimitBreak.Name=도끼 한계 돌파
-Axes.SubSkill.AxesLimitBreak.Description=한계를 돌파합니다. 강한 상대에 대한 추가 피해가 증가합니다. PVP를 위해 만들어졌으며, PVE에서 추가 피해를 줄지는 서버 설정에 따라 다릅니다.
+#참수
+Axes.Ability.Bonus.0=도끼 숙련
+Axes.Ability.Bonus.1=추가 {0} 피해
+Axes.Ability.Bonus.2=방어구 타격
+Axes.Ability.Bonus.3={0} 추가 피해를 방어구에 입힙니다.
+Axes.Ability.Bonus.4=강력한 타격
+Axes.Ability.Bonus.5={0} 추가 피해를 방어구가 없는 적에게 입힙니다.
+Axes.Ability.Lower=&7도끼를 내려놓습니다.
+Axes.Ability.Ready=&3도끼를 &6준비&3합니다.
+Axes.Ability.Ready.Extra=&3참수를 &6준비&3합니다. &7({0}이(가) {1}초 동안 재사용 대기 중입니다)
+Axes.Combat.CritStruck=&4치명적으로 맞았습니다!
+Axes.Combat.CriticalHit=치명타!
+Axes.Combat.GI.Proc=&a**강력한 힘으로 타격받았습니다**
+Axes.Combat.GI.Struck=**강력한 타격으로 맞았습니다**
+Axes.Combat.SS.Struck=&4해골 분쇄로 맞았습니다!
+Axes.SubSkill.SkullSplitter.Name=해골 분쇄
+Axes.SubSkill.SkullSplitter.Description=광역 피해를 입힙니다.
+Axes.SubSkill.SkullSplitter.Stat=해골 분쇄 지속 시간
+Axes.SubSkill.CriticalStrikes.Name=치명타
+Axes.SubSkill.CriticalStrikes.Description=두 배의 피해를 입힙니다.
+Axes.SubSkill.CriticalStrikes.Stat=치명타 확률
+Axes.SubSkill.AxeMastery.Name=참수 숙련
+Axes.SubSkill.AxeMastery.Description=추가 피해를 입힙니다.
+Axes.SubSkill.AxesLimitBreak.Name=참수 한계 돌파
+Axes.SubSkill.AxesLimitBreak.Description=한계를 돌파합니다. 강력한 적에 대한 추가 피해가 증가합니다. PVP를 위해 설계되었으며, PVE에서의 추가 피해 증가 여부는 서버 설정에 따릅니다.
Axes.SubSkill.AxesLimitBreak.Stat=한계 돌파 최대 피해
-
-#EXCAVATION
-Excavation.Ability.Lower=&7**삽 준비 해제**
-Excavation.Ability.Ready=&a**삽 준비 완료**
-Excavation.SubSkill.GigaDrillBreaker.Name=기가 드릴 버서커 (능력)
-Excavation.SubSkill.GigaDrillBreaker.Description=드롭 속도 3배, 경험치 3배, 속도 증가
-Excavation.SubSkill.TreasureHunter.Name=보물 사냥꾼
-Excavation.SubSkill.TreasureHunter.Description=보물 발굴 능력
-Excavation.Effect.Length=기가 드릴 버서커 지속시간: &e{0}초
-Excavation.Listener=발굴(EXCAVATION):
-Excavation.SkillName=발굴
-Excavation.Skills.GigaDrillBreaker.Off=**기가 드릴 버서커 발동 해제**
-Excavation.Skills.GigaDrillBreaker.On=&a**기가 드릴 버서커 발동**
-Excavation.Skills.GigaDrillBreaker.Refresh=&a당신의 &e기가 드릴 버서커 &a기술은 이제 사용 가능합니다!
-Excavation.Skills.GigaDrillBreaker.Other.Off={0}&2님은 &c기가 드릴 버서커를 사용했습니다!
-Excavation.Skills.GigaDrillBreaker.Other.On=&a{0}&2님은 &c기가 드릴 버서커를 사용 했습니다!
-Excavation.Skillup=발굴 기술이 {0} 올라 총 {1} 레벨이 되었습니다
-Excavation.SubSkill.GigaDrillBreaker.Stat=기가 드릴 버서커 지속시간
+Axes.SubSkill.ArmorImpact.Name=방어구 타격
+Axes.SubSkill.ArmorImpact.Description=충분한 힘으로 방어구를 깨뜨립니다.
+Axes.SubSkill.GreaterImpact.Name=강력한 타격
+Axes.SubSkill.GreaterImpact.Description=방어구가 없는 적에게 추가 피해를 입힙니다.
+Axes.Listener=참수:
+Axes.SkillName=참수
+Axes.Skills.SS.Off=**해골 분쇄 효과가 사라졌습니다**
+Axes.Skills.SS.On=&a**해골 분쇄 활성화됨**
+Axes.Skills.SS.Refresh=&a해당 &e해골 분쇄 &a기술이 회복되었습니다!
+Axes.Skills.SS.Other.Off=해골 분쇄&a 효과가 &e{0}에게서 사라졌습니다
+Axes.Skills.SS.Other.On=&a{0}&2이(가) &c해골 분쇄를 사용했습니다!
+#발굴
+Excavation.Ability.Lower=&7삽을 내려놓습니다.
+Excavation.Ability.Ready=&3삽을 &6준비&3합니다.
+Excavation.SubSkill.GigaDrillBreaker.Name=기가 드릴 브레이커
+Excavation.SubSkill.GigaDrillBreaker.Description=드롭 확률 3배, 경험치 3배, 속도 증가
+Excavation.SubSkill.GigaDrillBreaker.Stat=기가 드릴 브레이커 지속 시간
Excavation.SubSkill.Archaeology.Name=고고학
-Excavation.SubSkill.Archaeology.Description=땅의 비밀을 발굴하세요! 높은 기술 레벨은 보물을 찾을 때 경험치 구슬을 찾을 확률을 높입니다!
-Excavation.SubSkill.Archaeology.Stat=고고학 경험치 구슬 확률
-Excavation.SubSkill.Archaeology.Stat.Extra=고고학 경험치 구슬량
-
-#FISHING
-Fishing.Ability.Chance=입질 확률: &e{0}
-Fishing.Ability.Info=매직 헌터: &7 **트레져 헌터 랭크 향상**
-Fishing.Ability.Locked.0={0}레벨 때 기술 해제 (흔들기)
-Fishing.Ability.Locked.1={0}레벨 때 기술 해제 (얼음 낚시)
-Fishing.Ability.Locked.2={0}레벨 때 기술 해제 (낚시꾼 장인)
-Fishing.Ability.Rank=트레져 헌터 랭크: &e{0}/5랭크
-Fishing.Ability.TH.DropRate= 드롭 비율: &4함정: &e{0} &7공통: &e{1} &a비공통: &e{2}\n&9레어: &e{3} &d에픽: &e{4} &6레전드리: &e{5} &b레코드: &e{6}
-Fishing.Ability.TH.MagicRate=매직 헌터 확률: &e{0}
-Fishing.Ability.Shake=흔들기 확률: &e{0}
-Fishing.Ability.IceFishing=얼음 낚시: 얼음에서 낚시
-Fishing.Ability.FD=어부의 다이어트 랭크: &e{0}랭크
-Fishing.SubSkill.TreasureHunter.Name=트레져 헌터 (패시브)
-Fishing.SubSkill.TreasureHunter.Description=물건(그외) 낚시
-Fishing.SubSkill.MagicHunter.Name=매직 헌터
-Fishing.SubSkill.MagicHunter.Description=인챈트된 아이템 발견
-Fishing.SubSkill.Shake.Name=흔들기 (vs. 독립체)
-Fishing.SubSkill.Shake.Description=아이템을 몹이나 낚시에서 얻음
-Fishing.SubSkill.FishermansDiet.Name=어부의 다이어트
-Fishing.SubSkill.FishermansDiet.Description=어류 음식 허기 회복 증가
-Fishing.SubSkill.MasterAngler.Name=낚시꾼 장인
-Fishing.SubSkill.IceFishing.Name=얼음 낚시
-Fishing.SubSkill.IceFishing.Description=얼음이 덮혀있는 환경에서 낚시 가능
-Fishing.Chance.Raining=&9 비 특혜
-Fishing.Listener=낚시(FISHING):
-Fishing.Ability.TH.MagicFound=&7이 입질에서 마법이 느껴집니다...
-Fishing.Ability.TH.Boom=&7폭발 시간!!!
-Fishing.Ability.TH.Poison=&7낌새가 좋지 않습니다...
-Fishing.SkillName=낚시
-Fishing.Skillup=낚시 기술이 {0} 올라 총 {1} 레벨이 되었습니다
-Fishing.ScarcityTip=&e&o이 지역은 과잉 낚시로 인해 어필이 부족합니다. 더 많은 물고기를 잡으려면 다른 곳에 낚싯대를 던져보세요. 적어도 {0} 블록 이상 떨어진 곳에서 낚시하세요.
-Fishing.Scared=&7&o물고기를 놀라게 하는 불규칙한 움직임입니다!
-Fishing.Exhausting=&c&o낚싯대를 부적절하게 사용하면 피로가 쌓이고 낚싯대가 닳아버립니다!
-Fishing.LowResourcesTip=&7이 지역에는 물고기가 많이 남아있지 않을 것 같습니다. 적어도 {0} 블록 이상 떨어진 곳에서 낚시해보세요.
-Fishing.SubSkill.TreasureHunter.Stat=보물 사냥꾼 랭크: &a{0}&3/&a{1}
-Fishing.SubSkill.TreasureHunter.Stat.Extra=드롭 비율: &7일반: &e{0} &a희귀: &e{1}\n&9레어: &e{2} &d에픽: &e{3} &6레전드리: &e{4} &b신화: &e{5}
-Fishing.SubSkill.MagicHunter.Stat=매직 헌터 확률
+Excavation.SubSkill.Archaeology.Description=땅의 비밀을 밝혀냅니다! 높은 스킬 레벨은 보물을 찾을 때 경험치 오브를 발견할 확률을 높입니다!
+Excavation.SubSkill.Archaeology.Stat=고고학 경험치 오브 확률
+Excavation.SubSkill.Archaeology.Stat.Extra=고고학 경험치 오브 양
+Excavation.Listener=발굴:
+Excavation.SkillName=발굴
+Excavation.Skills.GigaDrillBreaker.Off=**기가 드릴 브레이커 효과가 사라졌습니다**
+Excavation.Skills.GigaDrillBreaker.On=&a**기가 드릴 브레이커 활성화됨**
+Excavation.Skills.GigaDrillBreaker.Refresh=&a해당 &e기가 드릴 브레이커 &a기술이 회복되었습니다!
+Excavation.Skills.GigaDrillBreaker.Other.Off=기가 드릴 브레이커&a 효과가 &e{0}에게서 사라졌습니다
+Excavation.Skills.GigaDrillBreaker.Other.On=&a{0}&2이(가) &c기가 드릴 브레이커를 사용했습니다!
+#낚시
+Fishing.ScarcityTip=&e&o이 지역은 낚시가 과잉되어 있습니다. 더 많은 물고기를 잡으려면 다른 곳에 낚싯대를 던지세요. 적어도 {0} 블록 떨어진 곳에서 낚시하세요.
+Fishing.Scared=&7&o난잡한 움직임은 물고기를 놀라게 합니다!
+Fishing.Exhausting=&c&o낚시대를 부적절하게 사용하면 피로가 쌓이고 낚시대가 닳습니다!
+Fishing.LowResourcesTip=&7이 지역에는 물고기가 많지 않을 것 같습니다. 적어도 {0} 블록 떨어진 곳에서 낚시하세요.
+Fishing.Ability.Info=마법사 사냥꾼: &7 **보물사냥꾼 랭크에 따라 향상됩니다**
+Fishing.Ability.Locked.0={0}+ 스킬까지 잠금 (흔들기)
+Fishing.Ability.Locked.1={0}+ 스킬까지 잠금 (얼음 낚시)
+Fishing.Ability.Locked.2={0}+ 스킬까지 잠금 (마스터 낚시꾼)
+Fishing.SubSkill.TreasureHunter.Name=보물사냥꾼
+Fishing.SubSkill.TreasureHunter.Description=다양한 물건을 낚아올립니다
+Fishing.SubSkill.TreasureHunter.Stat=보물사냥꾼 랭크: &a{0}&3/&a{1}
+Fishing.SubSkill.TreasureHunter.Stat.Extra=드롭 확률: &7흔함: &e{0} &a보통: &e{1}\n&9희귀: &e{2} &d에픽: &e{3} &6전설: &e{4} &b신화: &e{5}
+Fishing.SubSkill.MagicHunter.Name=마법사 사냥꾼
+Fishing.SubSkill.MagicHunter.Description=마법 부여된 아이템을 찾습니다
+Fishing.SubSkill.MagicHunter.Stat=마법사 사냥꾼 확률
+Fishing.SubSkill.Shake.Name=흔들기
+Fishing.SubSkill.Shake.Description=낚싯대로 몹이나 플레이어에서 아이템을 흔들어 떨구세요
Fishing.SubSkill.Shake.Stat=흔들기 확률
-Fishing.SubSkill.FishermansDiet.Stat=어부의 다이어트: &a랭크 {0}
-Fishing.SubSkill.MasterAngler.Description=어부가 더 자주 물고기를 잡습니다. 보트에서 낚시할 때 더 잘 작동합니다.
+Fishing.SubSkill.FishermansDiet.Name=어부의 식단
+Fishing.SubSkill.FishermansDiet.Description=낚은 음식의 포만감을 개선합니다
+Fishing.SubSkill.FishermansDiet.Stat=어부의 식단: &a랭크 {0}
+Fishing.SubSkill.MasterAngler.Name=마스터 낚시꾼
+Fishing.SubSkill.MasterAngler.Description=물고기를 더 자주 잡을 수 있으며, 배에서 낚시할 때 더 잘 작동합니다.
Fishing.SubSkill.MasterAngler.Stat=낚시 최소 대기 시간 감소: &a-{0} 초
Fishing.SubSkill.MasterAngler.Stat.Extra=낚시 최대 대기 시간 감소: &a-{0} 초
+Fishing.SubSkill.IceFishing.Name=얼음 낚시
+Fishing.SubSkill.IceFishing.Description=얼음 생물이 서식하는 생물꾼입니다
Fishing.SubSkill.IceFishing.Stat=얼음 낚시
-
-#HERBALISM
-Herbalism.Ability.DoubleDropChance=2배 드롭 확률: &e{0}
-Herbalism.Ability.FD=농부의 다이어트: &e{0}랭크
-Herbalism.Ability.GTe.Length=재배의 대지 지속시간: &e{0}초
-Herbalism.Ability.GTe.NeedMore=재배의 대지에 뿌릴 씨가 좀더 필요합니다.
-Herbalism.Ability.GTh.Chance=재배의 재능 확률: &e{0}
-Herbalism.Ability.GTh.Fail=**재배의 재능 실패**
-Herbalism.Ability.GTh.Stage=재배의 재능 단계: &e 작물 재배 {0}단계
-Herbalism.Ability.GTh=&a**재배의 재능**
-Herbalism.Ability.HylianLuck=하이랄인의 행운 확률: &e{0}
-Herbalism.Ability.Lower=&7**호미 준비 해제**
-Herbalism.Ability.Ready=&a**호미 준비 완료**
-Herbalism.Ability.ShroomThumb.Chance=버섯재배자의 숨결 확률: &e{0}
-Herbalism.Ability.ShroomThumb.Fail=**버섯재배자의 숨결 실패**
-Herbalism.SubSkill.GreenTerra.Name=재배의 대지 (능력)
-Herbalism.SubSkill.GreenTerra.Description=대지 뿌리기, 드롭 3배, 재배의 재능 부스트
-Herbalism.SubSkill.GreenThumb.Name=재배의 재능 (밀)
-Herbalism.SubSkill.GreenThumb.Description=수확시 자동 씨 심기
-Herbalism.Effect.4=재배의 재능 (블록들)
-Herbalism.SubSkill.GreenThumb.Description.2=이끼 낀 블록 만들기, 잔디 자라게 하기
-Herbalism.SubSkill.FarmersDiet.Name=농부의 다이어트
-Herbalism.SubSkill.FarmersDiet.Description=농작물 배고품 회복 향상
-Herbalism.SubSkill.DoubleDrops.Name=2배 드롭 (모든 작물)
-Herbalism.SubSkill.DoubleDrops.Description=항상 드롭 2배
-Herbalism.SubSkill.HylianLuck.Name=하이랄인의 행운
-Herbalism.SubSkill.HylianLuck.Description=적은 확률로 레어 아이템 얻음
-Herbalism.SubSkill.ShroomThumb.Name=버섯재배자의 숨결
-Herbalism.SubSkill.ShroomThumb.Description=흙 & 잔디에 균사체 살포
-Herbalism.HylianLuck=&a하이랄의 행운이 오늘 너에게 따르는구나!
-Herbalism.Listener=약초학(HERBALISM):
+Fishing.Chance.Raining=&9 비 보너스
+Fishing.Listener=낚시:
+Fishing.Ability.TH.MagicFound=&7이 잡은 물고기에는 마법의 손길을 느낍니다...
+Fishing.Ability.TH.Boom=&7폭발 시간!!!
+Fishing.Ability.TH.Poison=&7뭔가 이상한 냄새가 납니다...
+Fishing.SkillName=낚시
+#약초학
+Herbalism.Ability.GTe.NeedMore=더 많은 씨앗이 필요합니다. 녹색 에너지를 전파하기 위해서입니다.
+Herbalism.Ability.GTh.Fail=**식물재배 실패**
+Herbalism.Ability.GTh=&a**식물재배**
+Herbalism.Ability.Lower=&7국지를 낮춥니다.
+Herbalism.Ability.Ready=&3국지를 &6준비&3합니다.
+Herbalism.Ability.ShroomThumb.Fail=**버섯썸 실패**
+Herbalism.SubSkill.GreenTerra.Name=그린 테라
+Herbalism.SubSkill.GreenTerra.Description=테라를 전파하며, 드롭 3배 증가, 식물재배 강화
+Herbalism.SubSkill.GreenTerra.Stat=그린 테라 지속시간
+Herbalism.SubSkill.GreenThumb.Name=식물재배
+Herbalism.SubSkill.GreenThumb.Description=국지로 작물 수확 시 자동으로 심기
+Herbalism.SubSkill.GreenThumb.Stat=식물재배 확률
+Herbalism.SubSkill.GreenThumb.Stat.Extra=식물재배 단계: &a작물 단계 {0}에서 자랍니다
+Herbalism.Effect.4=식물재배 (블록)
+Herbalism.SubSkill.GreenThumb.Description.2=벽돌에 이끼를 더하거나 풀을 자라게 합니다
+Herbalism.SubSkill.FarmersDiet.Name=농부의 식단
+Herbalism.SubSkill.FarmersDiet.Description=농산물로부터 회복되는 허기가 향상됩니다
+Herbalism.SubSkill.FarmersDiet.Stat=농부의 식단: &a등급 {0}
+Herbalism.SubSkill.DoubleDrops.Name=더블 드롭
+Herbalism.SubSkill.DoubleDrops.Description=보통 드롭의 2배 획득
+Herbalism.SubSkill.DoubleDrops.Stat=더블 드롭 확률
+Herbalism.SubSkill.HylianLuck.Name=하이랄의 행운
+Herbalism.SubSkill.HylianLuck.Description=희귀 아이템을 찾을 작은 확률 제공
+Herbalism.SubSkill.HylianLuck.Stat=하이랄의 행운 확률
+Herbalism.SubSkill.ShroomThumb.Name=버섯썸
+Herbalism.SubSkill.ShroomThumb.Description=흙과 풀에 균사체를 전파합니다
+Herbalism.SubSkill.ShroomThumb.Stat=버섯썸 확률
+Herbalism.HylianLuck=&a하이라루의 행운이 오늘은 함께 합니다!
+Herbalism.Listener=약초학:
Herbalism.SkillName=약초학
-Herbalism.Skills.GTe.Off=**재배의 대지 비활성화됨**
-Herbalism.Skills.GTe.On=&a**재배의 대지 활성화됨**
-Herbalism.Skills.GTe.Refresh=&a당신의 &e재배의 대지 &a기술은 이제 사용 가능합니다!
-Herbalism.Skills.GTe.Other.Off={0}&2님은 &c재배의 대지를 사용했습니다!
-Herbalism.Skills.GTe.Other.On=&a{0}&2님은 &c재배의 대지를 사용했습니다!
-Herbalism.Skillup=약초학 기술이 {0} 올라 총 {1} 레벨이 되었습니다
-Herbalism.SubSkill.GreenTerra.Stat=재배의 대지 지속 시간
-Herbalism.SubSkill.GreenThumb.Stat=재배의 재능 확률
-Herbalism.SubSkill.GreenThumb.Stat.Extra=재배의 재능 단계: &a작물이 {0} 단계로 자라납니다
-Herbalism.SubSkill.FarmersDiet.Stat=농부의 다이어트: &a랭크 {0}
-Herbalism.SubSkill.DoubleDrops.Stat=2배 드롭 확률
-Herbalism.SubSkill.HylianLuck.Stat=하이랄인의 행운 확률
-Herbalism.SubSkill.ShroomThumb.Stat=버섯재배자의 숨결 확률
+Herbalism.Skills.GTe.Off=**그린 테라가 종료되었습니다**
+Herbalism.Skills.GTe.On=&a**그린 테라 활성화됨**
+Herbalism.Skills.GTe.Refresh=&a그린 테라 능력이 갱신되었습니다!
+Herbalism.Skills.GTe.Other.Off=&e{0}&a님의 그린 테라가 종료되었습니다
+Herbalism.Skills.GTe.Other.On=&a{0}&2님이 &c그린 테라를 사용했습니다!
+#채광
+Mining.Ability.Locked.0={0}+ 스킬이 필요합니다 (폭탄 채굴)
+Mining.Ability.Locked.1={0}+ 스킬이 필요합니다 (더 큰 폭탄)
+Mining.Ability.Locked.2={0}+ 스킬이 필요합니다 (폭파 전문가)
+Mining.Ability.Lower=&7곡괭이를 낮춥니다.
+Mining.Ability.Ready=&3곡괭이를 &6준비&3합니다.
+Mining.SubSkill.SuperBreaker.Name=슈퍼 브레이커
+Mining.SubSkill.SuperBreaker.Description=속도+, 드롭 3배 확률
+Mining.SubSkill.SuperBreaker.Stat=슈퍼 브레이커 지속시간
+Mining.SubSkill.DoubleDrops.Name=더블 드롭
+Mining.SubSkill.DoubleDrops.Description=보통 드롭의 2배 획득
+Mining.SubSkill.DoubleDrops.Stat=더블 드롭 확률
+Mining.SubSkill.BlastMining.Name=폭탄 채굴
+Mining.SubSkill.BlastMining.Description=TNT로 채굴 시 보너스 효과
+Mining.SubSkill.BlastMining.Stat=폭탄 채굴: &a등급 {0}/{1} &7({2})
+Mining.SubSkill.BlastMining.Stat.Extra=폭발 반경 증가: &a+{0}
+Mining.SubSkill.BiggerBombs.Name=더 큰 폭탄
+Mining.SubSkill.BiggerBombs.Description=TNT 폭발 반경 증가
+Mining.SubSkill.DemolitionsExpertise.Name=폭파 전문가
+Mining.SubSkill.DemolitionsExpertise.Description=TNT 폭발로 인한 피해 감소
+Mining.SubSkill.DemolitionsExpertise.Stat=폭파 전문가 피해 감소
-#MINING
-Mining.Ability.Length=파괴자 지속시간: &e{0}s
-Mining.Ability.Locked.0={0}레벨 때 기술 해제 (폭발 채굴)
-Mining.Ability.Locked.1={0}레벨 때 기술 해제 (거대 폭발)
-Mining.Ability.Locked.2={0}레벨 때 기술 해제 (전문 폭파)
-Mining.Ability.Lower=&7**곡괭이 준비 해제**
-Mining.Ability.Ready=&a**곡괭이 준비 완료**
-Mining.SubSkill.SuperBreaker.Name=파괴자 (능력)
-Mining.SubSkill.SuperBreaker.Description=속도 향상, 드롭 확률 3배
-Mining.SubSkill.DoubleDrops.Name=드롭 2배
-Mining.SubSkill.DoubleDrops.Description=항상 드롭 2배
-Mining.SubSkill.BlastMining.Name=폭발 채굴
-Mining.SubSkill.BlastMining.Description=TNT로 채굴시 추가 광물
-Mining.SubSkill.BiggerBombs.Name=거대 폭발
-Mining.SubSkill.BiggerBombs.Description=TNT 폭발거리 증가
-Mining.SubSkill.DemolitionsExpertise.Name=전문 폭파
-Mining.SubSkill.DemolitionsExpertise.Description=TNT 폭발 피해 감소
-Mining.Effect.Decrease=전문 폭파 피해 감소: &e{0}
-Mining.Effect.DropChance=드롭 2배 확률: &e{0}
-Mining.Listener=채광(MINING):
+Mining.Listener=Mining:
Mining.SkillName=채광
-Mining.Skills.SuperBreaker.Off=**파괴자 발동 해제**
-Mining.Skills.SuperBreaker.On=&a**파괴자 발동**
-Mining.Skills.SuperBreaker.Other.Off={0}&2님은 &c파괴자를 사용했습니다!
-Mining.Skills.SuperBreaker.Other.On=&a{0}&2님은 &c파괴자를 사용했습니다!
-Mining.Skills.SuperBreaker.Refresh=&a당신의 &e파괴자는 &a이제 사용 가능합니다!
-Mining.Skillup=채광 기술이 {0} 올라 총 {1} 레벨이 되었습니다
-Mining.SubSkill.SuperBreaker.Stat=파괴자 지속 시간
-Mining.SubSkill.DoubleDrops.Stat=드롭 2배 확률
-Mining.SubSkill.BlastMining.Stat=폭발 채광: &a랭크 {0}/{1} &7({2})
-Mining.SubSkill.BlastMining.Stat.Extra=폭발 범위 증가: &a+{0}
-Mining.SubSkill.DemolitionsExpertise.Stat=전문 폭파 피해 감소
-
-#폭발 채굴
-Mining.Blast.Boom=&7**폭발**
-Mining.Blast.Effect=+{0} 광물 이익, {1}x 드롭
-Mining.Blast.Radius.Increase=폭발 반경 증가: &e+{0}
-Mining.Blast.Rank=폭발 채굴: &e{0}/8랭크 &7({1})
-Mining.Blast.Other.On=&a{0}&2님은 &c폭발 채굴을 사용하셨습니다!
-Mining.Blast.Refresh=&a당신의 &e폭발 채굴 &a기술은 이제 사용 가능합니다!
-
+Mining.Skills.SuperBreaker.Off=**슈퍼 브레이커가 사라졌습니다**
+Mining.Skills.SuperBreaker.On=&a**슈퍼 브레이커가 활성화되었습니다**
+Mining.Skills.SuperBreaker.Other.Off=슈퍼 브레이커가 &e{0}님에게서 사라졌습니다
+Mining.Skills.SuperBreaker.Other.On=&a{0}&2님이 &c슈퍼 브레이커를 사용하였습니다!
+Mining.Skills.SuperBreaker.Refresh=&a당신의 &e슈퍼 브레이커&a 능력이 갱신되었습니다!
+Mining.Listener=Mining:
+Mining.SkillName=채광
+Mining.Skills.SuperBreaker.Off=슈퍼 브레이커가 사라졌습니다
+Mining.Skills.SuperBreaker.On=&a슈퍼 브레이커가 활성화되었습니다
+Mining.Skills.SuperBreaker.Other.Off=슈퍼 브레이커가 &e{0}님에게서 사라졌습니다
+Mining.Skills.SuperBreaker.Other.On=&a{0}&2님이 &c슈퍼 브레이커를 사용하였습니다!
+Mining.Skills.SuperBreaker.Refresh=&a당신의 &e슈퍼 브레이커&a 능력이 갱신되었습니다!
+#Blast Mining
+Mining.Blast.Boom=&7펑
+Mining.Blast.Cooldown=
+Mining.Blast.Effect=채굴량 +{0}, 드롭 횟수 {1}배 증가
+Mining.Blast.Other.On=&a{0}&2님이 &c폭발 채굴을 사용하였습니다!
+Mining.Blast.Refresh=&a폭발 채굴 능력이 갱신되었습니다!
#REPAIR
Repair.SubSkill.Repair.Name=수리
-Repair.SubSkill.Repair.Description=도구 & 방어구 수리
-Repair.SubSkill.GoldRepair.Name=금 수리 ({0}레벨 이상)
-Repair.SubSkill.GoldRepair.Description=금 도구 & 방어구 수리
-Repair.SubSkill.IronRepair.Name=철 수리 ({0}레벨 이상)
-Repair.SubSkill.IronRepair.Description=철 도구 & 방어구 수리
-Repair.SubSkill.StoneRepair.Name=돌 수리 ({0}레벨 이상)
+Repair.SubSkill.Repair.Description=도구 및 갑옷 수리
+Repair.SubSkill.GoldRepair.Name=금 수리 ({0}+ 레벨)
+Repair.SubSkill.GoldRepair.Description=금 도구 및 갑옷 수리
+Repair.SubSkill.IronRepair.Name=철 수리 ({0}+ 레벨)
+Repair.SubSkill.IronRepair.Description=철 도구 및 갑옷 수리
+Repair.SubSkill.StoneRepair.Name=돌 수리 ({0}+ 레벨)
Repair.SubSkill.StoneRepair.Description=돌 도구 수리
-Repair.SubSkill.RepairMastery.Name=수리 마스터리
-Repair.SubSkill.RepairMastery.Description=수리 양 증가
+Repair.SubSkill.RepairMastery.Name=수리 숙련
+Repair.SubSkill.RepairMastery.Description=수리량 증가
+Repair.SubSkill.RepairMastery.Stat=수리 숙련도: &a추가로 {0} 내구도 복구
Repair.SubSkill.SuperRepair.Name=슈퍼 수리
-Repair.SubSkill.SuperRepair.Description=효율 2배
-Repair.SubSkill.DiamondRepair.Name=다이아몬드 수리 ({0} 레벨)
-Repair.SubSkill.DiamondRepair.Description=다이아몬드 도구 & 방어구 수리
-Repair.SubSkill.ArcaneForging.Name=인챈트 아이템 수리
-Repair.SubSkill.ArcaneForging.Description=마법 아이템 수리
-Repair.Error=&4mcMMO이 아이템을 수리하려고 시도하는 동안 오류가 발생했습니다!
-Repair.Listener.Anvil=&4당신은 모루를 놓았습니다, 모루는 도구들과 방어구를 수리할 수 있습니다.
-Repair.Listener=수리(REPAIR):
-Repair.SkillName=수리
-Repair.Skills.AdeptDiamond=&4당신은 아직 다이아몬드를 수리할 수 있는 기술을 배우지 않았습니다.
-Repair.Skills.AdeptGold=&4당신은 아직 금을 수리할 수 있는 기술을 배우지 않았습니다.
-Repair.Skills.AdeptIron=&4당신은 아직 철을 수리할 수 있는 기술을 배우지 않았습니다.
-Repair.Skills.AdeptStone=&4당신은 아직 돌을 수리할 수 있는 기술을 배우지 않았습니다.
-Repair.Skills.Adept=당신은 &e{1}을/를 수리할려면 &e{0}&c레벨이 필요합니다
-Repair.Skills.FeltEasy=&7쉬운 느낌~
-Repair.Skills.FullDurability=&7내구도가 꽉 찼습니다.
-Repair.Skills.Mastery=수리 마스터리: &e추가 내구성 복구: {0}
-Repair.Skills.StackedItems=&4한번에 많은 아이템을 수리할 수 없습니다.
-Repair.Skills.Super.Chance=슈퍼 수리 확률: &e{0}
-Repair.Skillup=수리 기술이 {0} 올라 총 {1} 레벨이 되었습니다
-Repair.Pretty.Name=수리
-Repair.SubSkill.RepairMastery.Stat=수리 마스터리: &a추가 {0} 내구성 복구
+Repair.SubSkill.SuperRepair.Description=두 배로 효과 증가
Repair.SubSkill.SuperRepair.Stat=슈퍼 수리 확률
-Repair.SubSkill.ArcaneForging.Stat=인챈트 아이템 수리: &e랭크 {0}/{1}
-Repair.SubSkill.ArcaneForging.Stat.Extra=&3인챈트 아이템 수리 확률:&7 성공 &a{0}&7%, 실패 &c{1}&7%
-
+Repair.SubSkill.DiamondRepair.Name=다이아몬드 수리 ({0}+ 레벨)
+Repair.SubSkill.DiamondRepair.Description=다이아몬드 도구 및 갑옷 수리
+Repair.SubSkill.ArcaneForging.Name=신비한 대장간
+Repair.SubSkill.ArcaneForging.Description=마법 아이템 수리
+Repair.SubSkill.ArcaneForging.Stat=신비한 대장간: &e등급 {0}/{1}
+Repair.SubSkill.ArcaneForging.Stat.Extra=&3신비한 대장간 확률:&7 성공 &a{0}&7%, 실패 &c{1}&7%
+Repair.Error=&4mcMMO가 이 아이템을 수리하는 동안 오류가 발생했습니다!
+Repair.Listener.Anvil=&4당신은 모루를 배치했습니다. 모루는 도구와 갑옷을 수리할 수 있습니다.
+Repair.Listener=수리:
+Repair.SkillName=수리
+Repair.Skills.AdeptDiamond=&4다이아몬드 수리에 능숙하지 않습니다.
+Repair.Skills.AdeptGold=&4금 수리에 능숙하지 않습니다.
+Repair.Skills.AdeptIron=&4철 수리에 능숙하지 않습니다.
+Repair.Skills.AdeptStone=&4돌 수리에 능숙하지 않습니다.
+Repair.Skills.Adept=&c레벨 &e{0}&c 이상이어야 &e{1}&c을(를) 수리할 수 있습니다.
+Repair.Skills.FeltEasy=&7쉽게 느껴졌습니다.
+Repair.Skills.FullDurability=&7이 아이템은 완전한 내구도입니다.
+Repair.Skills.StackedItems=&4중첩된 아이템은 수리할 수 없습니다.
+Repair.Pretty.Name=수리
#Arcane Forging
-Repair.Arcane.Chance.Downgrade=&7인챈트 수리 격하 확률: &e{0}%
-Repair.Arcane.Chance.Success=&7인챈트 수리 성공 확률: &e{0}%
-Repair.Arcane.Downgrade=이 아이템의 인챈트는 감소했습니다.
-Repair.Arcane.Fail=이 아이템의 인챈트는 영구적으로 소멸되었습니다.
-Repair.Arcane.Lost=당신은 모든 인챈트를 유지할 기술이 충분치 않습니다.
-Repair.Arcane.Perfect=&a이 아이템의 인챈트를 지속시켰습니다.
-Repair.Arcane.Rank=인챈트 수리: &e{0}/{1}랭크
-
+Repair.Arcane.Downgrade=이 아이템의 신비한 힘이 감소되었습니다.
+Repair.Arcane.Fail=이 아이템의 신비한 힘이 영구히 사라졌습니다.
+Repair.Arcane.Lost=너무 능력이 부족하여 마법을 추출할 수 없었습니다.
+Repair.Arcane.Perfect=&a이 아이템의 신비한 힘을 유지하였습니다.
#SALVAGE
-Salvage.Pretty.Name=회수
-Salvage.SubSkill.AdvancedSalvage.Name=전문적인 회수
-Salvage.SubSkill.AdvancedSalvage.Description=손상된 아이템 회수
-Salvage.Ability.Locked.0={0} 레벨 때 기술해제 (전문적인 회수)
-Salvage.Ability.Bonus.0=전문적인 회수
-Salvage.Ability.Bonus.1=부셔진 아이템의 최대 추출량 {0}
-Salvage.Arcane.Rank=신비로운 회수: &eRank {0}/{1}
-Salvage.Arcane.ExtractFull=&7최대-인챈트 기회 부과
-Salvage.Arcane.ExtractPartial=&7일부-인챈트 기회 부과
-Salvage.Skills.Success=&a아이템 회수됨!
-Salvage.Skills.Adept.Damaged=&4손상된 아이템을 회수할 능력이 없습니다.
-Salvage.Skills.Adept.Level={1}를 &c회수하려면 &e{0}&c 레벨이 되야합니다
-Salvage.Skills.TooDamaged=&4이 아이템은 심하게 손상되어 회수할 수 없습니다.
-Salvage.Skills.ArcaneFailed=당신은 이 아이템 속의 지식을 추출할 수 없습니다.
-Salvage.Skills.ArcanePartial=당신은 이 아이템 속의 지식의 일부만 추출할 수 있었습니다.
-Salvage.Skills.ArcaneSuccess=&a당신은 이 아이템의 모든 지식을 추출할 수 있습니다!
-Salvage.Listener.Anvil=&4당신은 회수 모루를 놓았습니다, 도구나 방어구 회수에 쓰입니다.
-Salvage.Listener=회수(SALVAGE):
-Salvage.SkillName=회수
-Salvage.Pretty.Name=Salvage
+Salvage.Pretty.Name=분해
Salvage.SubSkill.UnderstandingTheArt.Name=예술의 이해
-Salvage.SubSkill.UnderstandingTheArt.Description=이웃의 쓰레기를 뒤지는 것뿐만 아니라 환경을 보호하는 것입니다.\n회수의 다양한 속성을 강화합니다.
+Salvage.SubSkill.UnderstandingTheArt.Description=이웃의 쓰레기를 털기 위한 것이 아니라 환경을 보호하기 위한 것입니다.\n분해의 다양한 속성을 강화합니다.
Salvage.SubSkill.ScrapCollector.Name=폐기물 수집가
-Salvage.SubSkill.ScrapCollector.Description=아이템에서 재료를 회수하며, 완벽한 회수는 기술과 운에 달려 있습니다.
-Salvage.SubSkill.ScrapCollector.Stat=폐기물 수집가: &a최대 &e{0}&a개의 아이템을 회수합니다. 운이 조금 관여됩니다.
-Salvage.SubSkill.ArcaneSalvage.Name=신비로운 회수
-Salvage.SubSkill.ArcaneSalvage.Description=아이템에서 인챈트 추출
-Salvage.SubSkill.ArcaneSalvage.Stat=신비로운 회수: &e랭크 {0}/{1}
-Salvage.Skills.Lottery.Normal=&6{1}&6에서 &3{0}&6개의 재료를 회수했습니다.
-Salvage.Skills.Lottery.Perfect=&a&l완벽합니다!&r&6{1}&6에서 수고 없이 &3{0}&6개의 재료를 회수했습니다.
-Salvage.Skills.Lottery.Untrained=&7회수에 적절하게 훈련되지 않았습니다. {1}&7에서는 &c{0}&7개의 재료만 회수할 수 있었습니다.
-
-#Anvil (Shared between SALVAGE and REPAIR)
-Anvil.Unbreakable=이 아이템은 Unbreakable입니다!
-
-#SWORDS
-Swords.Ability.Lower=&7**검 준비 해제**
-Swords.Ability.Ready=&a**검 준비 완료**
-Swords.Combat.Bleed.Chance=출혈 확률: &e{0}
-Swords.Combat.Bleed.Length=출혈 지속시간: &e{0} 틱
-Swords.Combat.Bleed.Note=&7알림: &e1 틱은 2초입니다
-Swords.Combat.Bleeding.Started=&4 당신은 피를 흘리고 있습니다!
-Swords.Combat.Bleeding.Stopped=&7출혈이 &a멈췄습니다&7!
-Swords.Combat.Bleeding=&a**출혈**
-Swords.Combat.Counter.Chance=카운터 어택 확률: &e{0}
-Swords.Combat.Counter.Hit=&4카운터 어택에 맞았습니다!
-Swords.Combat.Countered=&a**카운터-어택**
-Swords.Combat.SS.Struck=&4톱날 공격에 맞았습니다!
-Swords.SubSkill.CounterAttack.Name=카운터 어택
-Swords.SubSkill.CounterAttack.Description={0} 피해 반사
-Swords.SubSkill.SerratedStrikes.Name=톱날 공격 (능력)
-Swords.SubSkill.SerratedStrikes.Description=피해 {0} 증가, 출혈 증가
-Swords.Effect.4=톱날 공격 출혈 증가
-Swords.Effect.5={0} 틱 출혈
-Swords.SubSkill.Bleed.Name=출혈
-Swords.SubSkill.Bleed.Description=과다 출혈
-Swords.Listener=검술(SWORDS):
-Swords.SkillName=검술
-Swords.Skills.SS.Off=**톱날 공격 발동 해제**
-Swords.Skills.SS.On=&a**톱날 공격 발동**
-Swords.Skills.SS.Refresh=&a당신의 &e톱날 공격 &a스킬은 이제 사용 가능합니다!
-Swords.Skills.SS.Other.Off={0}&2님은 &c톱날 공격 스킬을 사용 해제했습니다!
-Swords.Skills.SS.Other.On=&a{0}&2님은 &c톱날 공격 스킬을 사용했습니다!
-Swords.Skillup=검술 스킬이 {0} 올라 총 {1} 레벨이 되었습니다
-Swords.SS.Length=톱날 공격 지속시간: &e{0}초
-Swords.Combat.Rupture.Note.Update.One=&7(파열 노트): 주기적인 피해는 치명적이지 않으며 초당 두 번 발생하며 방어구를 무시합니다.
+Salvage.SubSkill.ScrapCollector.Description=아이템에서 재료를 분해합니다. 완벽한 분해는 기술과 운에 달려있습니다.
+Salvage.SubSkill.ScrapCollector.Stat=폐기물 수집가: &a최대 &e{0}&a개의 아이템 분해. 일부 운이 필요합니다.
+Salvage.SubSkill.ArcaneSalvage.Name=신비한 분해
+Salvage.SubSkill.ArcaneSalvage.Description=아이템에서 마법을 추출합니다.
+Salvage.SubSkill.ArcaneSalvage.Stat=신비한 분해: &e등급 {0}/{1}
+Salvage.Ability.Bonus.0=폐기물 수집가
+Salvage.Ability.Bonus.1=최대 &e{0}&a개의 아이템 분해. 일부 운이 필요합니다.
+Salvage.Arcane.ExtractFull=&7신비한 분해 전체 마법 추출 확률
+Salvage.Arcane.ExtractPartial=&7신비한 분해 부분적 마법 추출 확률
+Salvage.Skills.Success=&a아이템을 분해하였습니다!
+Salvage.Skills.Adept.Damaged=&4손상된 아이템은 분해할 수 없습니다.
+Salvage.Skills.Adept.Level=레벨 &e{0}&c 이상이어야 &e{1}&c을(를) 분해할 수 있습니다.
+Salvage.Skills.TooDamaged=&4이 아이템은 너무 손상되었어 분해할 수 없습니다.
+Salvage.Skills.ArcaneFailed=&c이 아이템에 담긴 지식을 추출하지 못했습니다.
+Salvage.Skills.ArcanePartial=&c이 아이템에 담긴 일부 지식만 추출할 수 있었습니다.
+Salvage.Skills.ArcaneSuccess=&a이 아이템에 담긴 모든 지식을 추출할 수 있었습니다!
+Salvage.Listener.Anvil=&4당신은 분해 모루를 배치했습니다. 도구와 갑옷을 분해하려면 이를 사용하세요.
+Salvage.Listener=분해:
+Salvage.SkillName=분해
+Salvage.Skills.Lottery.Normal=&6{1}&6에서 &3{0}&6개의 재료를 분해하였습니다.
+Salvage.Skills.Lottery.Perfect=&a&l완벽해요!&r&6 &3{1}&6을 쉽게 회수하여 &3{0}&6 자료를 검색했습니다.
+Salvage.Skills.Lottery.Untrained=&7인양에 대한 교육을 제대로 받지 못했습니다. &a{1}&7에서 &c{0}&7 자료만 복구할 수 있었습니다.
+#모루(SALVAGE와 REPAIR 간에 공유)
+Anvil.Unbreakable=이 아이템은 깨지지 않습니다!
+#검
+Swords.Ability.Lower=&7검을 내려놓습니다.
+Swords.Ability.Ready=&3당신은 &6검을 준비합니다&3.
+Swords.Combat.Rupture.Note.Update.One=&7(파열 노트): 주기적인 피해는 매초 두 번 발생하며 방어구를 뚫고 발생합니다.
+Swords.Combat.Bleeding.Started=&4출혈 중입니다!
+Swords.Combat.Bleeding.Stopped=&7출혈이 &a중단&7되었습니다!
+Swords.Combat.Bleeding=&a**적이 출혈 중**
+Swords.Combat.Counter.Hit=&4반격으로 공격당했습니다!
+Swords.Combat.Countered=&a**반격당함**
+Swords.Combat.SS.Struck=&4톱니 모양의 타격을 받았습니다!
+Swords.SubSkill.CounterAttack.Name=반격
+Swords.SubSkill.CounterAttack.Description=공격당했을 때 일부 피해를 반사합니다!
Swords.SubSkill.CounterAttack.Stat=반격 확률
-Swords.SubSkill.SerratedStrikes.Stat=톱날 공격 지속시간
+Swords.SubSkill.SerratedStrikes.Name=톱니 모양 타격
+Swords.SubSkill.SerratedStrikes.Description=일정 범위 내에서 부분적인 피해를 입히며 파열을 적용할 확률이 있습니다!
+Swords.SubSkill.SerratedStrikes.Stat=톱니 모양 타격 길이
Swords.SubSkill.Rupture.Name=파열
Swords.SubSkill.Rupture.Description=폭발적으로 끝나는 지속 피해 효과
Swords.SubSkill.Stab.Name=찌르기
Swords.SubSkill.Stab.Description=공격에 추가 피해를 줍니다.
Swords.SubSkill.Stab.Stat=찌르기 피해
Swords.SubSkill.SwordsLimitBreak.Name=검 한계 돌파
-Swords.SubSkill.SwordsLimitBreak.Description=한계를 깨는 것. 강력한 상대에 대한 추가 피해. PVP를 위해 설계되었으며, PVE에서 피해 증가 여부는 서버 설정에 따릅니다.
+Swords.SubSkill.SwordsLimitBreak.Description=한계 돌파. 강한 상대에 대한 피해 증가. PVP용으로 의도되었으며 PVE에서 피해를 증가시킬 것인지는 서버 설정에 따라 다릅니다.
Swords.SubSkill.SwordsLimitBreak.Stat=한계 돌파 최대 피해
Swords.SubSkill.Rupture.Stat=파열 확률
-Swords.SubSkill.Rupture.Stat.Extra=[[DARK_AQUA]]파열 지속 시간: &e{0}초&a (플레이어), &e{1}초&a (몹).
-Swords.SubSkill.Rupture.Stat.TickDamage=[[DARK_AQUA]]파열 순수 틱 피해: &e{0}&a (플레이어), &e{1}&a (몹).
-Swords.SubSkill.Rupture.Stat.ExplosionDamage=[[DARK_AQUA]]파열 폭발 피해: &e{0}&a (플레이어), &e{1}&a (몹).
-
-#TAMING
+Swords.SubSkill.Rupture.Stat.Extra=[[DARK_AQUA]]파열 지속 시간: &e{0}초&a 플레이어 대상, &e{1}초&a 몹 대상.
+Swords.SubSkill.Rupture.Stat.TickDamage=[[DARK_AQUA]]파열 순수 틱 피해: &e{0}&a 플레이어 대상, &e{1}&a 몹 대상.
+Swords.SubSkill.Rupture.Stat.ExplosionDamage=[[DARK_AQUA]]파열 폭발 피해: &e{0}&a 플레이어 대상, &e{1}&a 몹 대상
+Swords.Effect.4=톱니 모양 타격 파열+
+Swords.Effect.5={0} 틱 파열
+Swords.Listener=검:
+Swords.SkillName=검술
+Swords.Skills.SS.Off=**톱니 모양 타격 효과가 사라졌습니다**
+Swords.Skills.SS.On=&a**톱니 모양 타격 활성화됨**
+Swords.Skills.SS.Refresh=&a당신의 &e톱니 모양 타격 &a능력이 갱신되었습니다!
+Swords.Skills.SS.Other.Off=톱니 모양 타격&a가 &e{0}에게 종료되었습니다
+Swords.Skills.SS.Other.On=&a{0}&2님이 &c톱니 모양 타격을 사용했습니다!
+#길들이기
Taming.Ability.Bonus.0=환경 인식
-Taming.Ability.Bonus.1=늑대 위험 회피
+Taming.Ability.Bonus.1=늑대가 위험을 피합니다
Taming.Ability.Bonus.2=두꺼운 털
-Taming.Ability.Bonus.3=1/{0} 피해, 내화성(불저항력)
-Taming.Ability.Bonus.4=충격 증명
-Taming.Ability.Bonus.5=항상 1/{0} 폭발 피해
-Taming.Ability.Bonus.6=날카로운 발톱
+Taming.Ability.Bonus.3=1/{0} 피해, 화염 저항
+Taming.Ability.Bonus.4=내구성이 좋음
+Taming.Ability.Bonus.5=폭발물의 일반 피해를 1/{0} 받습니다
+Taming.Ability.Bonus.6=올바르게 갈겨진 발톱
Taming.Ability.Bonus.7=+{0} 피해
-Taming.Ability.Bonus.8=빠른 음식 제공
-Taming.Ability.Bonus.9={0} 확률로 공격시 회복
-Taming.Ability.Bonus.10=신성한 사냥개
-Taming.Ability.Bonus.11=마법이나 독으로 인한 손상 회복
-Taming.Ability.Locked.0={0}레벨 때 스킬해제 (환경 인식)
-Taming.Ability.Locked.1={0}레벨 때 스킬해제 (두꺼운 털)
-Taming.Ability.Locked.2={0}레벨 때 스킬해제 (충격 증명)
-Taming.Ability.Locked.3={0}레벨 때 스킬해제 (날카로운 발톱)
-Taming.Ability.Locked.4={0}레벨 때 스킬해제 (빠른 음식 제공)
-Taming.Ability.Locked.5={0}레벨 때 스킬해제 (신성한 사냥개)
-Taming.Combat.Chance.Gore=돌진 확률: &e{0}
-Taming.SubSkill.BeastLore.Name=짐승의 포효
-Taming.SubSkill.BeastLore.Description=뼈로 늑대/오셀롯 검사
-Taming.SubSkill.ShockProof.Name=충격 방지
-Taming.SubSkill.ShockProof.Description=폭발 피해 절감
-Taming.SubSkill.CallOfTheWild.Name=야생의 포효
-Taming.SubSkill.CallOfTheWild.Description=옆에 동물 소환
-Taming.SubSkill.CallOfTheWild.Description.2=&7COTW (오셀롯): 쭈그리면서 물고기를 들고 {0}번 좌 클릭
-Taming.Effect.15=&7COTW (늑대): 쭈그리면서 뼈를 들고 {0}번 좌 클릭
-Taming.SubSkill.Gore.Name0=&7COTW (말): 쭈그리면서 사과를 들고 {0}번 좌 클릭
-Taming.SubSkill.FastFoodService.Name=빠른 음식 제공
-Taming.SubSkill.FastFoodService.Description=공격시 치료 기회
-Taming.SubSkill.HolyHound.Name=신성한 사냥개
-Taming.SubSkill.HolyHound.Description=마법 & 독 피해 치료
-Taming.SubSkill.Gore.Name=돌진
-Taming.SubSkill.Gore.Description=크리티컬 스크라이크 출혈 적용
-Taming.SubSkill.SharpenedClaws.Name=날카로운 발톱
-Taming.SubSkill.SharpenedClaws.Description=추가 피해
+Taming.Ability.Bonus.8=패스트 푸드 서비스
+Taming.Ability.Bonus.9=공격 시 회복 확률 {0}
+Taming.Ability.Bonus.10=성스러운 사냥개
+Taming.Ability.Bonus.11=마법 또는 독 피해를 받을 때 체력 회복
+Taming.Ability.Locked.0={0}+ 능력(환경 인식)까지 잠금 해제됨
+Taming.Ability.Locked.1={0}+ 능력(두꺼운 털)까지 잠금 해제됨
+Taming.Ability.Locked.2={0}+ 능력(내구성이 좋음)까지 잠금 해제됨
+Taming.Ability.Locked.3={0}+ 능력(올바르게 갈겨진 발톱)까지 잠금 해제됨
+Taming.Ability.Locked.4={0}+ 능력(패스트 푸드 서비스)까지 잠금 해제됨
+Taming.Ability.Locked.5={0}+ 능력(성스러운 사냥개)까지 잠금 해제됨
+Taming.Combat.Chance.Gore=피 피하기 확률
+Taming.SubSkill.BeastLore.Name=야수 지식
+Taming.SubSkill.BeastLore.Description=늑대와 오셀롯을 검토하면서 골격을 칩니다
+Taming.SubSkill.ShockProof.Name=내구성이 좋음
+Taming.SubSkill.ShockProof.Description=폭발 데미지 감소
+Taming.SubSkill.CallOfTheWild.Name=야생의 부름
+Taming.SubSkill.CallOfTheWild.Description=동물을 당신 곁으로 소환합니다
+Taming.SubSkill.CallOfTheWild.Description.2=&7COTW: 앉아서 왼쪽 버튼을 클릭하면\n {0} {1} (오셀롯), {2} {3} (늑대), {4} {5} (말)이 소환됩니다
+Taming.SubSkill.FastFoodService.Name=패스트 푸드 서비스
+Taming.SubSkill.FastFoodService.Description=늑대가 공격할 때 회복할 확률
+Taming.SubSkill.HolyHound.Name=성스러운 사냥개
+Taming.SubSkill.HolyHound.Description=마법 및 독으로 회복됩니다
+Taming.SubSkill.Gore.Name=피 피하기
+Taming.SubSkill.Gore.Description=파열을 적용하는 크리티컬 스트라이크
+Taming.SubSkill.SharpenedClaws.Name=올바르게 갈겨진 발톱
+Taming.SubSkill.SharpenedClaws.Description=피해 보너스
Taming.SubSkill.EnvironmentallyAware.Name=환경 인식
-Taming.SubSkill.EnvironmentallyAware.Description=선인장/용암 공포증, 낙사 피해 감소
+Taming.SubSkill.EnvironmentallyAware.Description=선인장/용암 공포증, 낙하 피해 면역
Taming.SubSkill.ThickFur.Name=두꺼운 털
-Taming.SubSkill.ThickFur.Description=피해 감소, 내화성(불저항력)
-Taming.Listener.Wolf=&8늑대가 당신에게 되돌아감...
-Taming.Listener=조련(TAMING):
-Taming.SkillName=조련
-Taming.Skillup=조련 스킬이 {0} 올라 총 {1} 레벨이 되었습니다
-Taming.Summon.Complete=&a소환 완료
-Taming.Summon.Fail.Ocelot=당신 근처에 이미 많은 오셀롯이 있어 더는 소환시킬 수 없습니다.
-Taming.Summon.Fail.Wolf=당신 근처에 이미 많은 늑대가 있어 더는 소환시킬 수 없습니다.
-Taming.Summon.Fail.Horse=당신 근처에 이미 많은 말이 있어 더는 소환시킬 수 없습니다.
-Taming.SubSkill.Pummel.Name=퍼멜
-Taming.SubSkill.Pummel.Description=당신의 늑대는 상대방을 넉백시킬 확률이 있습니다.
-Taming.SubSkill.Pummel.TargetMessage=늑대에 의해 넉백당했습니다!
-Taming.Summon.COTW.Success.WithoutLifespan=&a(Call Of The Wild) &7당신은 &6{0}&7을 소환했습니다.
-Taming.Summon.COTW.Success.WithLifespan=&a(Call Of The Wild) &7당신은 &6{0}&7을 소환했으며, 지속 시간은 &6{1}&7초입니다.
-Taming.Summon.COTW.Limit=&a(Call Of The Wild) &7동시에 소환할 수 있는 애완동물은 최대 &c{0}마리&7입니다.
-Taming.Summon.COTW.TimeExpired=&a(Call Of The Wild) &7시간이 지났습니다. 당신의 &6{0}&7이(가) 떠납니다.
-Taming.Summon.COTW.Removed=&a(Call Of The Wild) &7소환된 &6{0}&7이(가) 이 세상에서 사라졌습니다.
-Taming.Summon.COTW.BreedingDisallowed=&a(Call Of The Wild) &c소환된 동물은 번식할 수 없습니다.
-Taming.Summon.COTW.NeedMoreItems=&a(Call Of The Wild) &7더 필요한 아이템은 &e{0}&7개의 &3{1}&7입니다.
-Taming.Summon.Name.Format=&6(COTW) &f{0}의 {1}
-
-#UNARMED
-Unarmed.Ability.Berserk.Length=버서커 지속시간: &e{0}초
-Unarmed.Ability.Bonus.0=아이언 암 스타일
+Taming.SubSkill.ThickFur.Description=피해 감소, 화염 저항
+Taming.SubSkill.Pummel.Name=때리기
+Taming.SubSkill.Pummel.Description=늑대가 적을 밀치는 확률이 있습니다
+Taming.SubSkill.Pummel.TargetMessage=늑대에 의해 밀렸습니다!
+Taming.Listener.Wolf=&8당신의 늑대가 당신에게로 달려옵니다...
+Taming.Listener=길들이기:
+Taming.SkillName=길들이기
+Taming.Summon.COTW.Success.WithoutLifespan=&a(야생의 부름) &7당신은 &6{0}&7을(를) 소환했습니다
+Taming.Summon.COTW.Success.WithLifespan=&a(야생의 부름) &7당신은 &6{0}&7을(를) 소환했고 지속 시간은 &6{1}&7초입니다.
+Taming.Summon.COTW.Limit=&a(야생의 부름) &7동시에 &c{0} &7마리의 &7{1}을(를) 소환할 수 있습니다.
+Taming.Summon.COTW.TimeExpired=&a(야생의 부름) &7시간이 지나갔습니다, 당신의 &6{0}&7이(가) 떠납니다.
+Taming.Summon.COTW.Removed=&a(야생의 부름) &7당신이 소환한 &6{0}&7이(가) 이 세계에서 사라졌습니다.
+Taming.Summon.COTW.BreedingDisallowed=&a(야생의 부름) &c소환된 동물을 번식할 수 없습니다.
+Taming.Summon.COTW.NeedMoreItems=&a(야생의 부름) &7더 많은 &e{0}&7개의 &3{1}&7이(가) 필요합니다
+Taming.Summon.Name.Format=&6(야생의 부름) &f{0}의 {1}
+#비무장
+Unarmed.Ability.Bonus.0=강철 팔 스타일
Unarmed.Ability.Bonus.1=+{0} 피해 업그레이드
-Unarmed.Ability.Chance.ArrowDeflect=화살 회피 확률: &e{0}
-Unarmed.Ability.Chance.Disarm=비무장 확률: &e{0}
-Unarmed.Ability.Chance.IronGrip=강철 주먹 확률: &e{0}
-Unarmed.Ability.IronGrip.Attacker=상대는 강철 주먹을 가지고 있습니다!
-Unarmed.Ability.IronGrip.Defender=&a강철 주먹의 비무장을 일시적으로 방어했습니다!
-Unarmed.Ability.Lower=&7**손 준비 해제**
-Unarmed.Ability.Ready=&a**손 준비 완료**
-Unarmed.SubSkill.Berserk.Name=버서커 (능력)
-Unarmed.SubSkill.Berserk.Description=+50% 피해, 약한 광물들을 부숨
-Unarmed.SubSkill.Disarm.Name=비무장 (플레이어)
-Unarmed.SubSkill.Disarm.Description=적이 들고있는 아이템 드롭
-Unarmed.SubSkill.IronArmStyle.Name=강철 팔 형태
-Unarmed.SubSkill.IronArmStyle.Description=견고해지는 팔
-Unarmed.SubSkill.ArrowDeflect.Name=화살 회피
-Unarmed.SubSkill.ArrowDeflect.Description=회피 화살
-Unarmed.SubSkill.IronGrip.Name=아이언 그립
-Unarmed.SubSkill.IronGrip.Description=비무장 상태 방지
-Unarmed.Listener=비무장(UNARMED):
-Unarmed.SkillName=비무장
-Unarmed.Skills.Berserk.Off=**버서커 발동 해제**
-Unarmed.Skills.Berserk.On=&a**버서커 발동**
-Unarmed.Skills.Berserk.Other.Off={0}&2님은 &c버서커를 사용했습니다!
-Unarmed.Skills.Berserk.Other.On=&a{0}&2님은 &c버서커를 사용합니다!
-Unarmed.Skills.Berserk.Refresh=&a당신의 &e버서커 &a스킬은 이제 사용 가능합니다!
-Unarmed.Skillup=비무장 스킬이 {0} 올라 총 {1} 레벨이 되었습니다
-Unarmed.SubSkill.Berserk.Stat=버서커 지속시간
-Unarmed.SubSkill.Disarm.Stat=비무장 확률
+Unarmed.Ability.IronGrip.Attacker=상대방이 철제 손잡이를 가지고 있습니다!
+Unarmed.Ability.IronGrip.Defender=&a철제 손잡이 덕분에 무장 해제를 막았습니다!
+Unarmed.Ability.Lower=&7주먹을 내립니다.
+Unarmed.Ability.Ready=&3주먹을 &6준비합니다&3.
+Unarmed.SubSkill.Berserk.Name=광폭화
+Unarmed.SubSkill.Berserk.Description=+50% 피해, 약한 물체를 파괴합니다
+Unarmed.SubSkill.Berserk.Stat=광폭화 지속 시간
+Unarmed.SubSkill.Disarm.Name=무장 해제
+Unarmed.SubSkill.Disarm.Description=상대방이 손에 든 아이템을 떨어뜨립니다
+Unarmed.SubSkill.Disarm.Stat=무장 해제 확률
Unarmed.SubSkill.UnarmedLimitBreak.Name=비무장 한계 돌파
-Unarmed.SubSkill.UnarmedLimitBreak.Description=한계를 깨는 것. 강한 상대에 대한 향상된 피해. PVP를 위해 고안되었으며, PVE에서 피해 증가 여부는 서버 설정에 따라 다릅니다.
+Unarmed.SubSkill.UnarmedLimitBreak.Description=한계를 돌파합니다. 강력한 상대에 대한 피해가 증가합니다. PVP를 위해 의도되었으며 PVE에서 피해를 증가시킬 것인지는 서버 설정에 따릅니다.
Unarmed.SubSkill.UnarmedLimitBreak.Stat=한계 돌파 최대 피해
-Unarmed.SubSkill.SteelArmStyle.Name=강철 팔 형태
-Unarmed.SubSkill.SteelArmStyle.Description=견고해지는 팔
-Unarmed.SubSkill.ArrowDeflect.Stat=화살 회피 확률
-Unarmed.SubSkill.IronGrip.Stat=강철 주먹 확률
+Unarmed.SubSkill.SteelArmStyle.Name=강철 팔 스타일
+Unarmed.SubSkill.SteelArmStyle.Description=시간이 지남에 따라 팔을 강화합니다
+Unarmed.SubSkill.ArrowDeflect.Name=화살 방어
+Unarmed.SubSkill.ArrowDeflect.Description=화살을 튕깁니다
+Unarmed.SubSkill.ArrowDeflect.Stat=화살 방어 확률
+Unarmed.SubSkill.IronGrip.Name=철제 손잡이
+Unarmed.SubSkill.IronGrip.Description=무장 해제되지 않도록 합니다
+Unarmed.SubSkill.IronGrip.Stat=철제 손잡이 확률
Unarmed.SubSkill.BlockCracker.Name=블록 크래커
-Unarmed.SubSkill.BlockCracker.Description=주먹으로 바위 부수기
-
-#WOODCUTTING
-Woodcutting.Ability.0=나뭇잎 떨어트리기
-Woodcutting.Ability.1=나뭇잎 청소
-Woodcutting.Ability.Chance.DDrop=드롭 2배 확률: &e{0}
-Woodcutting.Ability.Length=나무꾼 지속시간: &e{0}초
-Woodcutting.Ability.Locked.0={0}레벨 때 스킬이 해제됩니다 (나뭇잎 떨어트리기)
-Woodcutting.SubSkill.TreeFeller.Name=나무꾼 (능력)
-Woodcutting.SubSkill.TreeFeller.Description=나무 폭발시키기
-Woodcutting.SubSkill.LeafBlower.Name=나뭇잎 떨어트리기
-Woodcutting.SubSkill.LeafBlower.Description=나뭇잎 청소
-Woodcutting.SubSkill.HarvestLumber.Name=드롭 2배
-Woodcutting.SubSkill.HarvestLumber.Description=항상 드롭 2배
-Woodcutting.Listener=벌목(WOODCUTTING):
-Woodcutting.SkillName=벌목
-Woodcutting.Skills.TreeFeller.Off=**나무꾼 발동 해제**
-Woodcutting.Skills.TreeFeller.On=&a**나무꾼 발동**
-Woodcutting.Skills.TreeFeller.Refresh=&a당신의 &e나무꾼 &a스킬은 이제 사용 가능합니다!
-Woodcutting.Skills.TreeFeller.Other.Off={0}&2님은 &c나무꾼 스킬을 사용 해제했습니다!
-Woodcutting.Skills.TreeFeller.Other.On=&a{0}&2님은 &c나무꾼 스킬을 사용했습니다!
-Woodcutting.Skills.TreeFeller.Splinter=도끼 파편 조각 수집!
-Woodcutting.Skills.TreeFeller.Threshold=그 나무는 너무 큽니다!
-Woodcutting.Skillup=벌목 스킬이 {0} 올라 총 {1} 레벨이 되었습니다
-Woodcutting.SubSkill.TreeFeller.Stat=나무꾼 지속시간
-Woodcutting.SubSkill.KnockOnWood.Name=나무 두드리기
-Woodcutting.SubSkill.KnockOnWood.Description=나무꾼을 사용할 때 추가 아이템을 찾을 수 있습니다.
-Woodcutting.SubSkill.KnockOnWood.Stat=나무 두드리기
-Woodcutting.SubSkill.KnockOnWood.Loot.Normal=나무에서 일반적인 아이템 획득
-Woodcutting.SubSkill.KnockOnWood.Loot.Rank2=나무에서 일반적인 아이템과 경험치 오브 획득
-Woodcutting.SubSkill.HarvestLumber.Stat=드롭 2배 확률
+Unarmed.SubSkill.BlockCracker.Description=주먹으로 바위를 부수세요
+Unarmed.Listener=비무장 전투:
+Unarmed.SkillName=비무장 전투
+Unarmed.Skills.Berserk.Off=**광폭화가 사라졌습니다**
+Unarmed.Skills.Berserk.On=&a**광폭화가 활성화되었습니다**
+Unarmed.Skills.Berserk.Other.Off=광폭화&a가 &e{0}에게 종료되었습니다
+Unarmed.Skills.Berserk.Other.On=&a{0}&2님이 &c광폭화를 사용했습니다!
+Unarmed.Skills.Berserk.Refresh=&a당신의 &e광폭화 &a능력이 갱신되었습니다!
+#나무꾼
+Woodcutting.Ability.0=잎 블로워
+Woodcutting.Ability.1=잎을 날려 버립니다
+Woodcutting.Ability.Locked.0={0}+ 기술(잎 블로워)까지 잠금 해제됨
+Woodcutting.SubSkill.TreeFeller.Name=나무 패러
+Woodcutting.SubSkill.TreeFeller.Description=나무를 폭발시킵니다
+Woodcutting.SubSkill.TreeFeller.Stat=나무 패러 지속 시간
+Woodcutting.SubSkill.LeafBlower.Name=잎 블로워
+Woodcutting.SubSkill.LeafBlower.Description=잎을 날려 버립니다
+Woodcutting.SubSkill.KnockOnWood.Name=나무를 두드립니다
+Woodcutting.SubSkill.KnockOnWood.Description=나무 패러를 사용할 때 추가 아이템을 찾을 수 있습니다
+Woodcutting.SubSkill.KnockOnWood.Stat=나무를 두드림
+Woodcutting.SubSkill.KnockOnWood.Loot.Normal=나무에서 표준 전리품
+Woodcutting.SubSkill.KnockOnWood.Loot.Rank2=나무에서 표준 전리품과 경험의 오브
+Woodcutting.SubSkill.HarvestLumber.Name=목재 수확
+Woodcutting.SubSkill.HarvestLumber.Description=숙련된 기술로 더 많은 목재를 추출합니다
+Woodcutting.SubSkill.HarvestLumber.Stat=더블 드롭 확률
Woodcutting.SubSkill.Splinter.Name=파편
-Woodcutting.SubSkill.Splinter.Description=나무를 더 효율적으로 베어내세요.
+Woodcutting.SubSkill.Splinter.Description=나무를 효율적으로 베어 내세요.
Woodcutting.SubSkill.BarkSurgeon.Name=나무 껍질 수술
Woodcutting.SubSkill.BarkSurgeon.Description=나무 껍질을 벗길 때 유용한 재료를 추출하세요.
-Woodcutting.SubSkill.NaturesBounty.Name=자연의 풍요
-Woodcutting.SubSkill.NaturesBounty.Description=자연에서 경험치를 모으세요.
-
-#ABILITIY
-
-#COMBAT
-Combat.ArrowDeflect=&f**화살 회피**
-Combat.BeastLore=&a**짐승의 포효**
-Combat.BeastLoreHealth=&3체력: (&a{0}&3/{1})
-Combat.BeastLoreOwner=&3주인: (&c{0}&3)
-Combat.Gore=&a**돌진**
-Combat.StruckByGore=**돌진에 맞았습니다**
-Combat.TargetDazed=목표가 &4혼란스러워합니다
-Combat.TouchedFuzzy=&4혼란이 일어났습니다. 아~ 어지러워.
+Woodcutting.SubSkill.NaturesBounty.Name=자연의 축복
+Woodcutting.SubSkill.NaturesBounty.Description=자연으로부터 경험치를 얻으세요.
+Woodcutting.Listener=나무꾼:
+Woodcutting.SkillName=나무꾼
+Woodcutting.Skills.TreeFeller.Off=**나무 패러가 사라졌습니다**
+Woodcutting.Skills.TreeFeller.On=&a**나무 패러가 활성화되었습니다**
+Woodcutting.Skills.TreeFeller.Refresh=&a당신의 &e나무 패러 &a능력이 갱신되었습니다!
+Woodcutting.Skills.TreeFeller.Other.Off=나무 패러&a가 &e{0}에게 종료되었습니다
+Woodcutting.Skills.TreeFeller.Other.On=&a{0}&2님이 &c나무 패러를 사용했습니다!
+Woodcutting.Skills.TreeFeller.Splinter=당신의 도끼가 수십 개의 조각으로 깨졌습니다!
+Woodcutting.Skills.TreeFeller.Threshold=그 나무는 너무 큽니다!
+#전투
+Combat.ArrowDeflect=&f**화살 방어**
+Combat.BeastLore=&a**야수 지식**
+Combat.BeastLoreHealth=&3체력 (&a{0}&3/{1})
+Combat.BeastLoreOwner=&3주인 (&c{0}&3)
Combat.BeastLoreHorseSpeed=&3말 이동 속도 (&a{0} 블록/초&3)
Combat.BeastLoreHorseJumpStrength=&3말 점프 강도 (&a최대 {0} 블록&3)
+Combat.Gore=&a**찔림**
+Combat.StruckByGore=**당신은 찔렸습니다**
+Combat.TargetDazed=대상이 &4현기를 잃었습니다
+Combat.TouchedFuzzy=&4털에 닿았습니다. 어지러웠습니다.
-#COMMANDS
-##generic
-mcMMO.Description=mcMMO&3 프로젝트에 대해서:,&6mcMMO는 한 &c오픈 소스&6 RPG 모드로 2011년 2월에 &9nossr50&6님이 만들었습니다. 목표는 질좋은 RPG 경험을 제공하는 것 입니다.,&3팁:,&6 - &c/mcmmo help&a 명령어들을 봅니다,&6 - &a타입 &c/스킬이름&a 자세한 스킬 정보를 봅니다,&3개발자들:,&6 - &anossr50 &9(제작자),&6 - &aGJ &9(프로젝트 주장),&6 - &aNuclearW &9(개발자),&6 - &abm01 &9(개발자),&6 - &aTfT_02 &9(개발자),&6 - &aGlitchfinder &9(개발자),&6 - &at00thpick1 &9(개발자),&3유용한 링크:,&6 - &ahttps://github.com/mcMMO-Dev/mcMMO/issues&6 버그 보고,&6 - &a#mcmmo @ irc.esper.net&6 IRC 채팅,
+#커멘드
+##일반적인
+mcMMO.Description=&3&emcMMO&3 프로젝트에 대해:,&6mcMMO는 2011년 2월에 &c오픈 소스&6 RPG 모드로 생성되었습니다,&6nossr50&6에 의해. 목표는 품질 높은 RPG 경험을 제공하는 것입니다.,&3팁:,&6 - &a/mcmmo help&a 명령어를 사용하여 명령어를 확인하세요,&6 - &a/SKILLNAME&a 을(를) 입력하여 자세한 스킬 정보를 확인하세요,&3개발자:,&6 - &anossr50 &9(창조자 및 프로젝트 리드),&6 - &aelectronicboy &9(개발자),&6 - &akashike &9(개발자),&6 - &at00thpick1 &9(클래식 관리자)
mcMMO.Description.FormerDevs=&3이전 개발자: &aGJ, NuclearW, bm01, TfT_02, Glitchfinder
-Commands.addlevels.AwardAll.1=&a당신은 모든 스킬에 {0} 레벨을 지급했습니다!
-Commands.addlevels.AwardAll.2=모든 스킬이 {0}로 변경되었습니다
-Commands.addlevels.AwardSkill.1=&a당신은 {0} 레벨을 {1}에 지급하였습니다!
-Commands.addlevels.AwardSkill.2={1} 님은 {0}을/를 수정하였습니다
-Commands.addxp.AwardAll=&a당신은 모든 스킬에 {0} 경험치를 지급했습니다!
-Commands.addxp.AwardSkill=&a당신은 {0} 경험치를 {1}에 지급하였습니다!
-Commands.Ability.Off=능력 사용이 &c꺼졌습니다
-Commands.Ability.On=능력 사용이 &a켜졌습니다
-Commands.Ability.Toggle=능력 사용은 &e{0}(으)로 전환되었습니다
-Commands.AdminChat.Off=관리자 채팅이 &c꺼졌습니다
-Commands.AdminChat.On=관리자 채팅이 &a켜졌습니다
-Commands.AdminToggle=&a- 관리자 채팅을 켜기/끄기합니다
-Commands.Chat.Console=*시스템*
-Commands.Cooldowns.Header=&6--= &amcMMO 능력 재 사용 대기시간&6 =--
-Commands.Cooldowns.Row.N=\ &c{0}&f - &6{1}초 남음
-Commands.Cooldowns.Row.Y=\ &b{0}&f - &2준비!
-Commands.Database.Cooldown=이 명령어를 다시 치기전에 1초를 기달려야만 합니다.
-Commands.Database.Processing=당신의 이전 명령어가 여전히 실행 중입니다. 기다려주세요.
-Commands.Database.CooldownMS=이 명령어를 다시 사용하기 전에 {0} 밀리초를 기다려야 합니다.
-Commands.Disabled=이 명령어는 비활성화 되있습니다.
-Commands.DoesNotExist= &c플레이어가 데이터베이스에 존재하지 않습니다!
-Commands.AdminChatSpy.Enabled=mcMMO 파티 채팅 감시가 활성화되었습니다.
-Commands.AdminChatSpy.Disabled=mcMMO 파티 채팅 감시가 비활성화되었습니다.
-Commands.AdminChatSpy.Toggle=mcMMO 파티 채팅 감시가 &e{0}&6님에 의해 토글되었습니다.
-Commands.AdminChatSpy.Chat=&6[감시: &a{0}&6] &f{1}
-Commands.GodMode.Disabled=mcMMO 불사신 모드 비활성화
-Commands.GodMode.Enabled=mcMMO 불사신 모드 활성화
-Commands.GodMode.Forbidden=[mcMMO] 이 월드에서 불사신 모드는 허용 금지입니다 (펄미션을 확인하세요)
-Commands.GodMode.Toggle=불사신 모드는 &e{0}&f(으)로 전환되었습니다
-Commands.Healthbars.Changed.HEARTS=[mcMMO] 당신의 체력바 보기 방식은 &c하트&f로 변경되었습니다.
-Commands.Healthbars.Changed.BAR=[mcMMO] 당신의 체력바 보기 방식은 &e박스&f로 변경되었습니다.
-Commands.Healthbars.Changed.DISABLED=[mcMMO] 당신의 몹 체력바는 &7비활성화&f 되었습니다.
-Commands.Healthbars.Invalid=잘못된 체력바 타입!
-Commands.Inspect=<플레이어> &a- 상세한 플레이어 정보를 봅니다
-Commands.Invite.Success=&a초대를 성공적으로 보냈습니다.
-Commands.Leaderboards=<스킬> <페이지> &a- mcMMO 스킬 정보
-Commands.mcc.Header=---[]&amcMMO 명령어&c[]---
-Commands.mcgod=&a- 불사신 모드 켜기/끄기
-Commands.mchud.Invalid=HUD 타입이 올바르지 않습니다.
-Commands.mcpurge.Success=&a데이터베이스가 성공적으로 초기화됬습니다!
+Commands.addlevels.AwardAll.1=&a모든 스킬에 {0} 레벨이 수여되었습니다!
+Commands.addlevels.AwardAll.2=모든 스킬에 {0}이(가) 수정되었습니다.
+Commands.addlevels.AwardSkill.1=&a{1} 스킬에서 {0} 레벨이 수여되었습니다!
+Commands.addlevels.AwardSkill.2={1}에서 {0}이(가) 수정되었습니다.
+Commands.addxp.AwardAll=&a모든 스킬에 {0} 경험이 주어졌습니다!
+Commands.addxp.AwardSkill=&a{1} 스킬에 {0} 경험이 주어졌습니다!
+Commands.Ability.Off=능력 사용이 &c비활성화&f되었습니다
+Commands.Ability.On=능력 사용이 &a활성화&f되었습니다
+Commands.Ability.Toggle=능력 사용이 &e{0}&f에 대해 전환되었습니다
+Commands.AdminChat.Off=어드민 채팅 전용 &c비활성화&f됨
+Commands.AdminChat.On=어드민 채팅 전용 &a활성화&f됨
+Commands.AdminToggle=&a- 어드민 채팅 전환
+Commands.Chat.Console=*콘솔*
+Commands.Cooldowns.Header=&6--= &amcMMO 능력 쿨다운&6 =--
+Commands.Cooldowns.Row.N=\ &c{0}&f - &6남은 시간: {1} 초
+Commands.Cooldowns.Row.Y=\ &b{0}&f - &2준비 완료!
+Commands.Database.CooldownMS=이 명령을 다시 사용하기 전에 {0} 밀리초를 기다려야 합니다.
+Commands.Database.Cooldown=이 명령을 다시 사용하기 전에 {0} 초를 기다려야 합니다.
+Commands.Database.Processing=이전 명령이 아직 처리 중입니다. 기다려 주십시오.
+Commands.Disabled=이 명령은 비활성화되었습니다.
+Commands.DoesNotExist= &c플레이어가 데이터베이스에 없습니다!
+Commands.GodMode.Disabled=mcMMO 갓 모드 비활성화됨
+Commands.GodMode.Enabled=mcMMO 갓 모드 활성화됨
+Commands.AdminChatSpy.Enabled=mcMMO 파티 채팅 감시 활성화됨
+Commands.AdminChatSpy.Disabled=mcMMO 파티 채팅 감시 비활성화됨
+Commands.AdminChatSpy.Toggle=mcMMO 파티 채팅이 &e{0}&f에 대해 전환되었습니다
+Commands.AdminChatSpy.Chat=&6[SPY: &a{0}&6] &f{1}
+Commands.GodMode.Forbidden=[mcMMO] 이 월드에서 갓 모드가 허용되지 않습니다 (권한 참조)
+Commands.GodMode.Toggle=갓 모드가 &e{0}&f에 대해 전환되었습니다
+Commands.Healthbars.Changed.HEARTS=[mcMMO] 건강 막대 표시 유형이 &c하트&f로 변경되었습니다.
+Commands.Healthbars.Changed.BAR=[mcMMO] 건강 막대 표시 유형이 &e상자&f로 변경되었습니다.
+Commands.Healthbars.Changed.DISABLED=[mcMMO] 몹의 건강 막대가 &7비활성화&f되었습니다.
+Commands.Healthbars.Invalid=유효하지 않은 건강 막대 유형입니다!
+Commands.Inspect=<플레이어> &a- 자세한 플레이어 정보 보기
+Commands.Invite.Success=&a초대가 성공적으로 전송되었습니다.
+Commands.Leaderboards=<스킬> <페이지> &a- 리더보드
+Commands.mcgod=&a- 갓 모드 전환
+Commands.mchud.Invalid=유효하지 않은 HUD 유형입니다.
+Commands.mcpurge.Success=&a데이터베이스가 성공적으로 정리되었습니다!
Commands.mcrank.Heading=&6-=개인 순위=-
-Commands.mcrank.Overall=종합&a - &6랭크 &f#&a{0}
-Commands.mcrank.Player=타겟: &f{0}
-Commands.mcrank.Skill={0}&a - &6랭크 &f#&a{1}
-Commands.mcrank.Unranked=&f랭크없음
-Commands.mcrefresh.Success={0}의 쿨다운이 초기화되었습니다.
-Commands.mcremove.Success=&a{0}님의 데이터베이스가 성공적으로 삭제되었습니다!
-Commands.mctop.Tip=&6팁: &c/mcrank&6 명령어를 사용하면 모든 개인 순위를 볼수 있습니다!
-Commands.mmoedit=[플레이어] <스킬> <새값> &a - 대상을 수정합니다
-Commands.mmoedit.AllSkills.1=&a당신의 모든 스킬 레벨이 {0}로 설정되었습니다!
-Commands.mmoedit.Modified.1=&a당신의 {0} 레벨이 {1}로 설정되었습니다!
-Commands.mmoedit.Modified.2={0}님은 {1}를 수정했습니다.
-Commands.mcconvert.Database.Same=당신은 이미 {0} 데이터베이스를 사용중입니다!
-Commands.mcconvert.Database.InvalidType={0} 은/는 잘못된 데이터베이스 타입입니다.
-Commands.mcconvert.Database.Start=&7{0}에서 {1}(으)로 전환 시작중...
-Commands.mcconvert.Database.Finish=&7데이터베이스 이동 완료; {1} 데이터베이스는 이제 {0} 데이터베이스로부터 모든 자료를 가집니다.
-Commands.mmoshowdb=현재 사용하는 데이터베이스: &a{0}
-Commands.mcconvert.Experience.Invalid=잘못된 공식 타입! 올바른 타입: &aLINEAR &c그리고 &aEXPONENTIAL.
-Commands.mcconvert.Experience.Same=이미 {0} 공식을 사용중입니다
-Commands.mcconvert.Experience.Start=&7{0} 에서 {1} 곡선으로 변환 시작
-Commands.mcconvert.Experience.Finish=&7공식 변환 완료; 이제 {0} XP 곡선입니다.
-Commands.ModDescription=&a- 플러그인에 대한 정보를 봅니다
-Commands.NoConsole=이 명령어는 콘솔에서의 사용을 지원하지 않습니다.
-Commands.Notifications.Off=능력 알림이 &c켜졌습니다
-Commands.Notifications.On=능력 알림이 &a꺼졌습니다
-Commands.Offline=이 명령어는 오프라인 플레이어에게 동작하지 않습니다.
-Commands.NotLoaded=플레이어 프로파일을 아직 불러오지 못했습니다.
-Commands.Other=---[]&a기타 명령어&c[]---
-Commands.Party.Header=-----[]&a파티&c[]-----
-Commands.Party.Features.Header=-----[]&a특징&c[]-----
+Commands.mcrank.Overall=종합&a - &6순위 &f#&a{0}
+Commands.mcrank.Player=&e{0}에 대한 순위
+Commands.mcrank.Skill=&e{0}&a - &6순위 &f#&a{1}
+Commands.mcrank.Unranked=&f순위 없음
+Commands.mcrefresh.Success={0}의 쿨다운이 새로 고쳐졌습니다.
+Commands.mcremove.Success=&a{0}가 데이터베이스에서 성공적으로 제거되었습니다!
+Commands.mctop.Tip=&6팁: 모든 개인 순위를 보려면 &c/mcrank&6을(를) 사용하세요!
+Commands.mmoedit=[플레이어] <스킬> <새값> &a - 대상 수정
+Commands.mmoedit.AllSkills.1=모든 스킬의 레벨이 {0}(으)로 설정되었습니다!
+Commands.mmoedit.Modified.1={0}의 레벨이 {1}(으)로 설정되었습니다!
+Commands.mmoedit.Modified.2={0}이(가) {1}로 수정되었습니다.
+Commands.mcconvert.Database.Same=이미 {0} 데이터베이스를 사용 중입니다!
+Commands.mcconvert.Database.InvalidType={0}은(는) 유효하지 않은 데이터베이스 유형입니다.
+Commands.mcconvert.Database.Start=&7{0}에서 {1}(으)로 변환 시작 중...
+Commands.mcconvert.Database.Finish=&7데이터베이스 마이그레이션이 완료되었습니다; {1} 데이터베이스에는 이제 {0} 데이터베이스의 모든 데이터가 포함되어 있습니다.
+Commands.mmoshowdb=현재 사용 중인 데이터베이스는 &a{0}&f입니다
+Commands.mcconvert.Experience.Invalid=알 수 없는 공식 유형! 유효한 유형은 다음과 같습니다: &aLINEAR 및 &aEXPONENTIAL.
+Commands.mcconvert.Experience.Same=이미 {0} 공식 유형을 사용 중입니다
+Commands.mcconvert.Experience.Start=&7{0}에서 {1} 곡선으로 변환 시작 중
+Commands.mcconvert.Experience.Finish=&7공식 변환 완료; 이제 {0} 경험 곡선을 사용합니다.
+Commands.ModDescription=&a- 간단한 모드 설명 읽기
+Commands.NoConsole=이 명령은 콘솔 사용을 지원하지 않습니다.
+Commands.Notifications.Off=능력 알림이 &c비활성화&f되었습니다
+Commands.Notifications.On=능력 알림이 &a활성화&f되었습니다
+Commands.Offline=이 명령은 오프라인 플레이어에게 작동하지 않습니다.
+Commands.NotLoaded=플레이어 프로필이 아직 로드되지 않았습니다.
Commands.Party.Status=&8이름: &f{0} {1} &8레벨: &3{2}
Commands.Party.Status.Alliance=&8동맹: &f{0}
-Commands.Party.UnlockedFeatures=&8해제된 특징: &7&o{0}
+Commands.Party.UnlockedFeatures=&8잠금 해제된 기능: &7&o{0}
Commands.Party.ShareMode=&8공유 모드:
Commands.Party.ItemShare=&7아이템 &3({0})
-Commands.Party.ExpShare=&7EXP &3({0})
-Commands.Party.ItemShareCategories=&8공유중인 아이템: &7&o{0}
-Commands.Party.MembersNear=&8당신의 근처 &3{0}&8/&3{1}
-Commands.Party.Accept=&a- 파티 초대 허용
-Commands.Party.Chat.Off=파티 채팅을 &c끕니다
-Commands.Party.Chat.On=파티 채팅을 &a켭니다
-Commands.Party.Commands=---[]&a파티 명령어&c[]---
-Commands.Party.Invite.0=알림: &a당신은 {1} 님으로부터 {0} 파티 초대에 권유받았습니다
-Commands.Party.Invite.1=타입 &a/party accept&e 명령어를 치면 파티 초대에 승낙됩니다
-Commands.Party.Invite=<플레이어> &a- 파티 초대를 보냅니다
-Commands.Party.Invite.Accepted=&a초대 수락됨. 당신은 {0} 파티에 가입되었습니다
-Commands.Party.Join=참여된 파티: {0}
-Commands.Party.Create=&7만들어진 파티: {0}
-Commands.Party.Rename=&7변경된 파티 이름: &f{0}
+Commands.Party.ExpShare=&7경험치 &3({0})
+Commands.Party.ItemShareCategories=&8아이템 공유: &7&o{0}
+Commands.Party.MembersNear=&8근처 플레이어 &3{0}&8/&3{1}
+Commands.Party.Accept=&a- 파티 초대 수락
+Commands.Party.Chat.Off=파티 채팅 전용 &c비활성화
+Commands.Party.Chat.On=파티 채팅 전용 &a활성화
+Commands.Party.Commands=&c---[]&a파티 명령어&c[]---
+Commands.Party.Invite.0=&c알림: &a{1}님이 {0}으로부터 파티 초대를 받았습니다.
+Commands.Party.Invite.1=&e파티 초대를 수락하려면 &a/party accept&e를 입력하세요.
+Commands.Party.Invite=&a- 파티 초대 전송
+Commands.Party.Invite.Accepted=&a초대가 수락되었습니다. {0} 파티에 가입했습니다.
+Commands.Party.Join=&7파티에 가입: {0}
+Commands.Party.PartyFull=&6{0}&c 파티가 가득 찼습니다!
+Commands.Party.PartyFull.Invite=이미 &a{1}&c에 &e{0}&c을(를) 초대했습니다. 이미 &3{2}&c명이 이 파티에 있습니다!
+Commands.Party.PartyFull.InviteAccept=&a{0}&c 파티에 가입할 수 없습니다. 이미 &3{1}&c명이 이 파티에 있습니다!
+Commands.Party.Create=&7파티 생성: {0}
+Commands.Party.Rename=&7파티 이름이 다음으로 변경되었습니다: &f{0}
Commands.Party.SetSharing=&7파티 {0} 공유 설정: &3{1}
-Commands.Party.ToggleShareCategory=&7파티 아이템 공유 &6{0} &7가 &3{1}되었습니다
-Commands.Party.AlreadyExists=&4{0} 파티은/는 이미 존재합니다!
-Commands.Party.Kick=당신은 {0} 파티에서 추방 당하였습니다.
-Commands.Party.Leave=파티를 떠났습니다
-Commands.Party.Members.Header=-----[]&a맴버들&c[]-----
-Commands.Party.None=당신은 파티에 참여되어 있지 않습니다.
-Commands.Party.Quit=&a- 현재 참여 되어있는 파티를 나갑니다
-Commands.Party.Teleport=&a- 파티 맴버한테 텔레포트합니다
-Commands.Party.Toggle=&a- 파티 채팅을 켜기/끄기 합니다
-Commands.Party1=&a- 새 파티를 만듭니다
-Commands.Party2=&a- 플레이어가 파티에 가입합니다
-Commands.Party.Alliance.Header=-----[]&a파티 동맹&c[]-----
-Commands.Party.Alliance.Ally=&f{0} &8파티의 동맹: &f{1}
-Commands.Party.Alliance.Members.Header=-----[]&a동맹 구성원&c[]-----
-Commands.Party.Alliance.Invite.0=알림: &a{1} 파티로부터 {0} 파티와의 동맹 초대를 받았습니다
-Commands.Party.Alliance.Invite.1=타입 &a/party alliance accept&e 초대에 수락합니다
-Commands.Party.Alliance.Invite.Accepted=&a동맹 초대 수락됨.
-Commands.Party.Alliance.None=당신은 동맹을 가지고 있지 않습니다.
-Commands.Party.Alliance.AlreadyAllies=당신의 파티는 이미 동맹을 가지고 있습니다. 관계를 해지하려면 &3/party alliance disband
-Commands.Party.Alliance.Help.0=이 파티는 동맹 형태를 가지고 있지 않습니다. 파티장을 초대하세요
-Commands.Party.Alliance.Help.1= 동맹을 하려면 &3/party alliance invite &c.
-Commands.ptp.Enabled=파티 텔레포트 &a활성화됨
-Commands.ptp.Disabled=파티 텔레포트 &c비활성화됨
-Commands.ptp.NoRequests=당신은 이 시간에 텔레포트 요청을 하실 수 없습니다
-Commands.ptp.NoWorldPermissions=[mcMMO] 당신은 월드 {0}(으)로 텔레포트할 권한이 없습니다.
-Commands.ptp.Request1={0} &a님이 당신에게 텔레포트를 신청했습니다.
-Commands.ptp.Request2=&a텔레포트하려면, 타입 &e/ptp accept&a. &c{0}&a초에 요청이 만기됩니다.
+Commands.Party.ToggleShareCategory=&7파티 &6{0} &7의 아이템 공유가 &3{1}&7로 변경되었습니다.
+Commands.Party.AlreadyExists=&4파티 {0}이(가) 이미 존재합니다!
+Commands.Party.Kick=&c{0}&c님이 파티에서 추방되었습니다: &a{1}&c!
+Commands.Party.Leave=&e현재 파티를 나갔습니다
+Commands.Party.Members.Header=&c-----[]&a파티 멤버&c[]-----
+Commands.Party.None=&c파티에 속해 있지 않습니다.
+Commands.Party.Quit=&a- 현재 파티 나가기
+Commands.Party.Teleport=&a- 파티 멤버로 이동
+Commands.Party.Toggle=&a- 파티 채팅 전환
+Commands.Party1=&a- 새로운 파티 생성
+Commands.Party2=&a- 플레이어의 파티 가입
+Commands.Party.Alliance.Header=&c-----[]&a파티 동맹&c[]-----
+Commands.Party.Alliance.Ally=&f{0} &8동맹: &f{1}
+Commands.Party.Alliance.Members.Header=&c-----[]&a동맹 멤버&c[]-----
+Commands.Party.Alliance.Invite.0=알림: &a{1}님이 {0}으로부터 파티 동맹 초대를 받았습니다.
+Commands.Party.Alliance.Invite.1=파티 동맹 초대를 수락하려면 &a/party alliance accept&e를 입력하세요.
+Commands.Party.Alliance.Invite.Accepted=&a파티 동맹 초대가 수락되었습니다.
+Commands.Party.Alliance.None=&c파티에 동맹이 없습니다.
+Commands.Party.Alliance.AlreadyAllies=&c파티에 이미 동맹이 있습니다. &3/party alliance disband&c로 해체하세요.
+Commands.Party.Alliance.Help.0=&c이 파티는 아직 동맹을 맺지 않았습니다. 파티 리더를 초대하여
+Commands.Party.Alliance.Help.1=&c 동맹을 맺으세요. &3/party alliance invite <플레이어>&c.
+Commands.ptp.Enabled=파티 텔레포팅 &a활성화됨
+Commands.ptp.Disabled=파티 텔레포팅 &c비활성화됨
+Commands.ptp.NoRequests=&c현재 텔레포트 요청이 없습니다.
+Commands.ptp.NoWorldPermissions=&c[mcMMO] 월드 {0}로 텔레포트할 권한이 없습니다.
+Commands.ptp.Request1=&e{0} &a님이 당신에게 텔레포트를 요청했습니다.
+Commands.ptp.Request2=&a텔레포트하려면 &e/ptp accept&a를 입력하세요. 요청은 &c{0} &a초 후 만료됩니다.
Commands.ptp.AcceptAny.Enabled=파티 텔레포트 요청 확인 &a활성화됨
Commands.ptp.AcceptAny.Disabled=파티 텔레포트 요청 확인 &c비활성화됨
-Commands.ptp.RequestExpired=파티 텔레포트 요청이 만기됨!
-Commands.PowerLevel.Leaderboard=--mcMMO&9 총 레벨 &e점수표--
-Commands.PowerLevel.Capped=&4총 레벨: &a{0} &4최대 레벨: &e{1}
-Commands.PowerLevel=&4총 레벨: &a{0}
-Commands.Reset.All=&a당신의 모든 스킬이 성공적으로 초기화되었습니다.
-Commands.Reset.Single=&a당신의 {0} 스킬이 성공적으로 초기화되었습니다.
-Commands.Reset=&a스킬 레벨을 0으로 초기화 시킵니다
-Commands.Scoreboard.Clear=&3mcMMO 점수판 청소됨.
-Commands.Scoreboard.NoBoard=mcMMO 점수판이 활성화 되어있지 않음.
-Commands.Scoreboard.Keep=&3mcMMO 점수판은 당신이 &a/mcscoreboard clear&3를 사용할 때까지 유지될 것임.
-Commands.Scoreboard.Timer=&3mcMMO 점수판은 지금으로부터 &6{0}&3초 내에 청소될 예정임.
-Commands.Scoreboard.Help.0=&6 == &c/mcscoreboard &a도움말&6 ==
-Commands.Scoreboard.Help.1=&3/mcscoreboard&b clear &f - McMMO 점수판을 청소함
-Commands.Scoreboard.Help.2=&3/mcscoreboard&b keep &f - McMMO 점수판을 유지함
-Commands.Scoreboard.Help.3=&3/mcscoreboard&b time [n] &f - McMMO 점수판을 &dn&f초 후에 청소함
-Commands.Scoreboard.Tip.Keep=&6팁: &c/mcscoreboard keep&6 점수판을 보이게 항상 유지.
-Commands.Scoreboard.Tip.Clear=&6팁: &c/mcscoreboard clear&6 점수판 감춤.
-Commands.Skill.Invalid=잘못된 스킬 이름 입니다!
-Commands.Skill.ChildSkill=이 명령어에는 부가 스킬을 사용할 수 없습니다!
-Commands.Skill.Leaderboard=--mcMMO &9{0}&e 점수표--
-Commands.SkillInfo=&a- 스킬에 대한 자세한 정보를 봅니다
-Commands.Stats.Self=당신의 통계
-Commands.Stats=&a- 당신의 mcMMO 통계 보기
-Commands.ToggleAbility=&a- 우클릭시 사용되는 스킬들을 켜기/끄기 합니다
-Commands.Usage.0=올바른 사용법 /{0}
-Commands.Usage.1=올바른 사용법 /{0} {1}
-Commands.Usage.2=올바른 사용법 /{0} {1} {2}
-Commands.Usage.3=올바른 사용법 /{0} {1} {2} {3}
-Commands.Usage.3.XP=&c올바른 사용법은 /{0} {1} {2} {3}&7입니다 (명령어를 실행한 플레이어에게 알리지 않고 실행하려면 끝에 -s를 포함시킬 수 있습니다)
-Commands.Usage.FullClassName=클레스이름
+Commands.ptp.RequestExpired=&c파티 텔레포트 요청이 만료되었습니다!
+Commands.PowerLevel.Leaderboard=&e--mcMMO&9 파워 레벨 &e리더보드--
+Commands.PowerLevel.Capped=&4파워 레벨: &a{0} &4최대 레벨: &e{1}
+Commands.PowerLevel=&4파워 레벨: &a{0}
+Commands.Reset.All=&a모든 스킬 레벨이 성공적으로 재설정되었습니다.
+Commands.Reset.Single=&a{0} 스킬 레벨이 성공적으로 재설정되었습니다.
+Commands.Reset=&a- 스킬 레벨을 0으로 재설정
+Commands.Scoreboard.Clear=&3mcMMO 스코어보드가 지워졌습니다.
+Commands.Scoreboard.NoBoard=&cmcMMO 스코어보드가 활성화되지 않았습니다.
+Commands.Scoreboard.Keep=&3mcMMO 스코어보드가 사용 중입니다. &a/mcscoreboard clear&3를 사용하여 제거하세요.
+Commands.Scoreboard.Timer=&3mcMMO 스코어보드가 &6{0}&3초 후에 사라집니다.
+Commands.Scoreboard.Help.0=&6 == &a/mcscoreboard&6 도움말 ==
+Commands.Scoreboard.Help.1=&3/mcscoreboard&b clear &f - mcMMO 스코어보드를 지웁니다.
+Commands.Scoreboard.Help.2=&3/mcscoreboard&b keep &f - mcMMO 스코어보드를 유지합니다.
+Commands.Scoreboard.Help.3=&3/mcscoreboard&b time [n] &f - mcMMO 스코어보드를 &d[n]&f초 후에 지웁니다.
+Commands.Scoreboard.Tip.Keep=&6팁: 스코어보드가 표시된 상태에서 &c/mcscoreboard keep&6을 사용하여 보여지도록 유지하세요.
+Commands.Scoreboard.Tip.Clear=&6팁: 스코어보드를 제거하려면 &c/mcscoreboard clear&6를 사용하세요.
+Commands.XPBar.Reset=&6mcMMO의 XP 바 설정이 재설정되었습니다.
+Commands.XPBar.SettingChanged=&6{0}&a의 XP 바 설정이 &a{1}&a(으)로 변경되었습니다.
+Commands.Skill.Invalid=유효하지 않은 스킬명입니다!
+Commands.Skill.ChildSkill=이 명령어에 대한 하위 스킬은 유효하지 않습니다!
+Commands.Skill.Leaderboard=--mcMMO &9{0}&e 리더보드--
+Commands.SkillInfo=&a- 스킬 또는 기능에 대한 자세한 정보 보기
+Commands.Stats=&a- 당신의 mcMMO 스탯 보기
+Commands.ToggleAbility=&a- 우클릭으로 능력 활성화/비활성화 토글
+Commands.Usage.0=&c올바른 사용법은 /{0}입니다.
+Commands.Usage.1=&c올바른 사용법은 /{0} {1}입니다.
+Commands.Usage.2=&c올바른 사용법은 /{0} {1} {2}입니다.
+Commands.Usage.3=&c올바른 사용법은 /{0} {1} {2} {3}입니다.
+Commands.Usage.3.XP=&c올바른 사용법은 /{0} {1} {2} {3}&7입니다 (플레이어에게 알리지 않고 명령어를 실행하려면 마지막에 -s를 포함시킵니다).
+Commands.Usage.FullClassName=클래스명
Commands.Usage.Level=레벨
-Commands.Usage.Message=메세지
+Commands.Usage.Message=메시지
Commands.Usage.Page=페이지
Commands.Usage.PartyName=이름
Commands.Usage.Password=비밀번호
Commands.Usage.Player=플레이어
-Commands.Usage.Rate=배율
+Commands.Usage.Rate=비율
Commands.Usage.Skill=스킬
-Commands.Usage.SubSkill=부가 스킬
-Commands.Usage.XP=xp
-Commands.XPBar.Reset=&6mcMMO의 XP 바 설정이 초기화되었습니다.
-Commands.XPBar.SettingChanged=&6{0}&6의 XP 바 설정이 &a{1}&6로 변경되었습니다.
-Commands.Description.mmoinfo=스킬 또는 메커니즘에 대한 자세한 정보를 읽습니다.
-Commands.MmoInfo.Mystery=&7아직 이 스킬을 해금하지 않았지만, 해금하면 여기에서 자세한 정보를 읽을 수 있습니다!
-Commands.MmoInfo.NoMatch=해당 부가 스킬이 존재하지 않습니다!
+Commands.Usage.SubSkill=하위 스킬
+Commands.Usage.XP=경험치
+Commands.Description.mmoinfo=스킬 또는 기능에 대한 자세한 정보를 읽으세요.
+Commands.MmoInfo.Mystery=&7아직이 스킬을 잠금 해제하지 않았지만, 잠금 해제하면 여기에서 자세한 정보를 읽을 수 있습니다!
+Commands.MmoInfo.NoMatch=그런 하위 스킬은 존재하지 않습니다!
Commands.MmoInfo.Header=&3-=[]=====[]&6 MMO 정보 &3[]=====[]=-
Commands.MmoInfo.SubSkillHeader=&6이름:&e {0}
-Commands.MmoInfo.DetailsHeader=&3-=[]=====[]&a 자세한 내용 &3[]=====[]=-
-Commands.MmoInfo.OldSkill=&7mcMMO 스킬은 개선된 모듈식 스킬 시스템으로 변환되고 있습니다. 불행히도 이 스킬은 아직 변환되지 않았으며 자세한 통계가 없습니다. 새로운 시스템은 새로운 mcMMO 스킬을 더 빠르게 출시하고 기존 스킬에 대한 유연성을 높일 수 있게 해줍니다.
-Commands.MmoInfo.Mechanics=&3-=[]=====[]&6 메커니즘 &3[]=====[]=-
+Commands.MmoInfo.DetailsHeader=&3-=[]=====[]&a 상세 정보 &3[]=====[]=-
+Commands.MmoInfo.OldSkill=&7mcMMO 스킬은 개선된 모듈식 스킬 시스템으로 변환되고 있으며, 이 스킬은 아직 변환되지 않아 상세한 통계가 없습니다. 새로운 시스템에서는 새로운 mcMMO 스킬의 빠른 출시와 기존 스킬의 유연성이 향상될 것입니다.
+Commands.MmoInfo.Mechanics=&3-=[]=====[]&6 기계 &3[]=====[]=-
Commands.MmoInfo.Stats=통계: {0}
-Commands.Mmodebug.Toggle=mcMMO 디버그 모드가 &6{0}&7되었습니다. 디버그 모드를 활성화하면 지원을 위해 유용한 정보를 얻기 위해 블록을 클릭할 수 있습니다.
-mcMMO.NoInvites=이 시간에 당신은 초대하지 못합니다
-mcMMO.NoPermission=&4권한이 부족합니다.
-mcMMO.NoSkillNote=&8만약 당신이 스킬을 사용할 수 없다면 여기에 표시되지 않습니다.
-
-
-
-##party
-Party.Forbidden=[mcMMO] 이 월드에서 파티를 하실 수 없습니다 (펄미션을 확인하세요)
-Party.Help.0=올바른 사용법 &3{0} <플레이어> [비밀번호].
-Party.Help.1=파티를 만들려면, &3{0} <이름> [비밀번호].
-Party.Help.2=파티 정보를 볼려면 &3{0}
-Party.Help.3=파티에 가입할려면 &3{0} <플레이어> [비밀번호] &c나갈려면 &3{1}
-Party.Help.4=파티를 잠금/잠금해제 할려면, &3{0}
-Party.Help.5=비밀번호로 파티를 보호할려면, &3{0} <비밀번호>
-Party.Help.6=파티에서 플레이어를 추방시킬려면, &3{0} <플레이어>
-Party.Help.7=파티장을 교체할려면, &3{0} <플레이어>
-Party.Help.8=파티를 해체할려면, &3{0}
-Party.Help.9=파티 맴버들과 아이템을 공유하려면 &3{0}
-Party.Help.10=파티 맴버들과 경험치 공유를 활성화화려면 &3{0}
-Party.InformedOnJoin={0} &a님이 당신의 파티에 참여했습니다
-Party.InformedOnQuit={0} &a님이 당신의 파티에서 떠났습니다
-Party.InformedOnNameChange=&6{0} &a님이 파티 이름을 &f{1}로 설정했습니다
-Party.InvalidName=&4잘못된 파티 이름입니다.
-Party.Invite.Self=자기자신을 초대할 수는 없습니다!
-Party.IsLocked=이 파티는 이미 잠겨져 있습니다!
-Party.IsntLocked=이 파티는 잠겨져 있지 않습니다!
-Party.Locked=파티가 잠겼습니다, 오직 파티장만이 초대를 할 수 있습니다.
-Party.NotInYourParty=&4{0}님은 당신의 파티에 없습니다
-Party.NotOwner=&4당신은 파티장이 아닙니다.
-Party.Target.NotOwner=&4{0}님은 파티장이 아닙니다.
-Party.Owner.New=&a{0}님이 새 파티장이 되었습니다.
-Party.Owner.NotLeader=&4당신은 이제 파티장이 아닙니다.
-Party.Owner.Player =&a당신은 이제 파티장입니다.
-Party.Password.None=이 파티는 비밀번호로 보호되고 있습니다. 가입할때 비밀번호를 제공해주세요.
-Party.Password.Incorrect=파티 비밀번호가 올바르지 않습니다.
-Party.Password.Set=&a설정한 파티 비밀번호는 {0} 입니다
-Party.Password.Removed=&a파티 비밀번호가 청소되었습니다.
-Party.Player.Invalid=그 플레이어는 올바르지 않습니다.
-Party.NotOnline=&4{0}님은 접속중이 아닙니다!
-Party.Player.InSameParty={0}님은 이미 당신의 파티에 있습니다!
-Party.PlayerNotInParty=&4{0}님은 파티에 없습니다
-Party.Specify=당신은 파티를 명기해야합니다.
-Party.Teleport.Dead=당신은 죽은 플레이어에게로 텔레포트 할 수 없습니다.
-Party.Teleport.Hurt=당신은 마지막으로 {0}초에 다쳐 텔레포트 할 수 없습니다.
-Party.Teleport.Player=&a당신은 {0}로 텔레포트했습니다.
-Party.Teleport.Self=자기자신한테 텔레포트 할 수 없습니다!
-Party.Teleport.Target=&a{0}님이 당신에게로 텔레포트했습니다.
-Party.Teleport.Disabled={0}님은 파티 텔레포트를 허용하고 있지 않습니다.
-Party.Rename.Same=이미 당신의 파티 이름입니다!
-Party.Join.Self=자기자신을 가입시킬수 없습니다!
-Party.Unlocked=&7파티가 잠금해제 되었습니다
-Party.Disband=&7그 파티가 해체되었습니다
-Party.Alliance.Formed=&7당신의 파티는 이제 &a{0} 파티와 동맹입니다
-Party.Alliance.Disband=&7당신의 파티는 더 이상 &c{0} 파티와 동맹이 아닙니다
-Party.Status.Locked=&4(초대만-허용)
-Party.Status.Unlocked=&2(개방)
-Party.LevelUp=파티 레벨이 {0} 올라 총 {1} 레벨이 되었습니다
+Commands.Mmodebug.Toggle=mcMMO 디버그 모드가 이제 &6{0}&7입니다. 디버그 모드가 true이면 지원에 사용되는 유용한 정보를 얻기 위해 블록을 두드릴 수 있습니다.
+mcMMO.NoInvites=&c현재 초대가 없습니다.
+mcMMO.NoPermission=&4권한이 충분하지 않습니다.
+mcMMO.NoSkillNote=&8해당 스킬에 액세스 권한이 없으면 여기에 표시되지 않습니다.
+##파티
+Party.Forbidden=[mcMMO] 이 월드에서는 파티가 허용되지 않습니다 (권한 확인).
+Party.Help.0=&c올바른 사용법은 &3{0} <플레이어> [비밀번호]입니다.
+Party.Help.1=&c파티를 생성하려면 &3{0} <이름> [비밀번호]를 사용하세요.
+Party.Help.2=&c자세한 정보는 &3{0} &c를 참조하세요.
+Party.Help.3=&c가입하려면 &3{0} <플레이어> [비밀번호]&c를 사용하고 나가려면 &3{1} &c를 사용하세요.
+Party.Help.4=&c파티를 잠그거나 잠금 해제하려면 &3{0} &c를 사용하세요.
+Party.Help.5=&c파티에 비밀번호를 설정하려면 &3{0} <비밀번호>&c를 사용하세요.
+Party.Help.6=&c플레이어를 파티에서 추방하려면 &3{0} <플레이어>&c를 사용하세요.
+Party.Help.7=&c파티 소유권을 이전하려면 &3{0} <플레이어>&c를 사용하세요.
+Party.Help.8=&c파티를 해체하려면 &3{0} &c를 사용하세요.
+Party.Help.9=&c파티 멤버와 아이템을 공유하려면 &3{0} &c를 사용하세요.
+Party.Help.10=&c파티 멤버와 경험치를 공유하려면 &3{0} &c를 사용하세요.
+Party.InformedOnJoin={0} &a님이 당신의 파티에 가입했습니다
+Party.InformedOnQuit={0} &a님이 당신의 파티를 나갔습니다
+Party.InformedOnNameChange=&6{0} &a님이 파티 이름을 &f{1}&a(으)로 설정했습니다
+Party.InvalidName=&4유효하지 않은 파티 이름입니다.
+Party.Invite.Self=&c자신을 초대할 수 없습니다!
+Party.IsLocked=&c이 파티는 이미 잠겨 있습니다!
+Party.IsntLocked=&c이 파티는 잠겨 있지 않습니다!
+Party.Locked=&c파티가 잠겨 있어서, 파티 리더만 초대할 수 있습니다.
+Party.NotInYourParty=&4{0}님이 당신의 파티에 속해 있지 않습니다.
+Party.NotOwner=&4파티 리더가 아닙니다.
+Party.Target.NotOwner=&4{0}님이 파티 리더가 아닙니다.
+Party.Owner.New=&a{0}님이 새로운 파티 리더가 되었습니다.
+Party.Owner.NotLeader=&4더 이상 파티 리더가 아닙니다.
+Party.Owner.Player =&a파티 리더가 되었습니다.
+Party.Password.None=&c파티에 비밀번호가 설정되어 있습니다. 가입하려면 비밀번호를 제공하세요.
+Party.Password.Incorrect=&c파티 비밀번호가 잘못되었습니다.
+Party.Password.Set=&a파티 비밀번호가 &a{0}&a(으)로 설정되었습니다.
+Party.Password.Removed=&a파티 비밀번호가 지워졌습니다.
+Party.Player.Invalid=&c유효하지 않은 플레이어입니다.
+Party.NotOnline=&4{0}님이 오프라인입니다!
+Party.Player.InSameParty=&c{0}님은 이미 당신의 파티에 속해 있습니다!
+Party.PlayerNotInParty=&4{0}님이 파티에 속해 있지 않습니다.
+Party.Specify=&c파티를 지정해야 합니다.
+Party.Teleport.Dead=&c죽은 플레이어에게 텔레포트할 수 없습니다.
+Party.Teleport.Hurt=&c최근 {0}초 동안 다치셨기 때문에 텔레포트할 수 없습니다.
+Party.Teleport.Player=&a당신이 {0}님에게 텔레포트하였습니다.
+Party.Teleport.Self=&c자신에게 텔레포트할 수 없습니다!
+Party.Teleport.Target=&a{0}님이 당신에게 텔레포트하였습니다.
+Party.Teleport.Disabled=&c{0}님이 파티 텔레포트를 허용하지 않습니다.
+Party.Rename.Same=&c이미 해당 파티의 이름입니다!
+Party.Join.Self=&c자신에게 가입할 수 없습니다!
+Party.Unlocked=&7파티가 잠금 해제되었습니다
+Party.Disband=&7파티가 해체되었습니다
+Party.Alliance.Formed=&7당신의 파티가 이제 &a{0}&7님과 동맹 관계입니다
+Party.Alliance.Disband=&7당신의 파티는 더 이상 &c{0}&7님과 동맹 관계가 아닙니다
+Party.Status.Locked=&4(초대 전용)
+Party.Status.Unlocked=&2(공개)
+Party.LevelUp=&e파티 레벨이 {0}만큼 증가하였습니다. 전체 ({1})
Party.Feature.Chat=파티 채팅
Party.Feature.Teleport=파티 텔레포트
Party.Feature.Alliance=동맹
Party.Feature.ItemShare=아이템 공유
-Party.Feature.XpShare=경험치 공유
-Party.Feature.Locked.Chat={0}레벨 때 스킬해제 (파티 채팅)
-Party.Feature.Locked.Teleport={0}레벨 때 스킬해제 (파티 텔레포트)
-Party.Feature.Locked.Alliance={0}레벨 때 스킬해제 (동맹)
-Party.Feature.Locked.ItemShare={0}레벨 때 스킬해제 (아이템 공유)
-Party.Feature.Locked.XpShare={0}레벨 때 스킬해제 (경험치 공유)
-Party.Feature.Disabled.1=파티 채팅은 아직 해제되지 않았습니다.
-Party.Feature.Disabled.2=파티 텔레포트는 아직 해제되지 않았습니다.
-Party.Feature.Disabled.3=파티 동맹은 아직 해제되지 않았습니다.
-Party.Feature.Disabled.4=아이템 공유는 아직 해제되지 않았습니다.
-Party.Feature.Disabled.5=경험치 공유는 아직 해제되지 않았습니다.
-Party.ShareType.Xp=경험치
+Party.Feature.XpShare=XP 공유
+Party.Feature.Locked.Chat=잠금 해제까지 {0}+ (파티 채팅)
+Party.Feature.Locked.Teleport=잠금 해제까지 {0}+ (파티 텔레포트)
+Party.Feature.Locked.Alliance=잠금 해제까지 {0}+ (동맹)
+Party.Feature.Locked.ItemShare=잠금 해제까지 {0}+ (아이템 공유)
+Party.Feature.Locked.XpShare=잠금 해제까지 {0}+ (XP 공유)
+Party.Feature.Disabled.1=&c파티 채팅이 아직 잠금 해제되지 않았습니다.
+Party.Feature.Disabled.2=&c파티 텔레포트가 아직 잠금 해제되지 않았습니다.
+Party.Feature.Disabled.3=&c파티 동맹이 아직 잠금 해제되지 않았습니다.
+Party.Feature.Disabled.4=&c파티 아이템 공유가 아직 잠금 해제되지 않았습니다.
+Party.Feature.Disabled.5=&c파티 XP 공유가 아직 잠금 해제되지 않았습니다.
+Party.ShareType.Xp=XP
Party.ShareType.Item=아이템
Party.ShareMode.None=없음
-Party.ShareMode.Equal=균등
-Party.ShareMode.Random=무작위
-Party.ItemShare.Category.Loot=강탈
-Party.ItemShare.Category.Mining=채광
-Party.ItemShare.Category.Herbalism=약초학
-Party.ItemShare.Category.Woodcutting=벌목
+Party.ShareMode.Equal=동등
+Party.ShareMode.Random=랜덤
+Party.ItemShare.Category.Loot=전리품
+Party.ItemShare.Category.Mining=채굴
+Party.ItemShare.Category.Herbalism=허브 수확
+Party.ItemShare.Category.Woodcutting=나무 벌채
Party.ItemShare.Category.Misc=기타
-
##xp
-Commands.XPGain.Acrobatics=떨어지기
-Commands.XPGain.Alchemy=포션 양조하기
-Commands.XPGain.Archery=몬스터 공격하기
-Commands.XPGain.Axes=몬스터 공격하기
-Commands.XPGain.Child=상위 스킬들로 부터 레벨들을 얻습니다
-Commands.XPGain.Excavation=땅 파거나 보물 발견하기
-Commands.XPGain.Fishing=낚시하기
-Commands.XPGain.Herbalism=식물 수집하기
-Commands.XPGain.Mining=돌이나 광석 캐기
-Commands.XPGain.Repair=수리하기
-Commands.XPGain.Swords=몬스터 공격하기
-Commands.XPGain.Taming=동물을 조련하거나, 조련된 동물로 사냥하기
-Commands.XPGain.Unarmed=몬스터 공격하기
-Commands.XPGain.Woodcutting=나무 자르기
-Commands.XPGain=&8경험치 얻는 방법: &f{0}
-Commands.xplock.locked=&6당신의 경험치 바는 {0}로 잠겼습니다!
-Commands.xplock.unlocked=&6당신의 경험치 바는 &a잠금 해제되었습니다&6!
-Commands.xprate.modified=경험치 배율이 {0}배로 수정되었습니다
-Commands.xprate.over=mcMMO 경험치 이벤트가 종료되었습니다!!
-Commands.xprate.proper.0=경험치 배율 이벤트를 사용법: &f/xprate <배율>
-Commands.xprate.proper.1=경험치 배율을 초기화 방법: &f/xprate reset
-Commands.xprate.proper.2=이것은 XP 이벤트인지 아닌지 true 또는 false로 나타내기 위해 지정하십시오
-Commands.xprate.started.0=&6mcMMO 경험치 이벤트가 시작되었습니다!
-Commands.xprate.started.1=&6mcMMO 경험치 배율은 {0}배 입니다!
-XPRate.Event= &6mcMMO 는 현재 경험치 이벤트 중입니다! 경험치는 {0}배 입니다!
-Commands.NegativeNumberWarn=마이너스 숫자는 허용되지 않습니다!
-Commands.Event.Start=&amcMMO&6 이벤트 시작!
+Commands.XPGain.Acrobatics=낙하
+Commands.XPGain.Alchemy=포션 제조
+Commands.XPGain.Archery=몬스터 공격
+Commands.XPGain.Axes=몬스터 공격
+Commands.XPGain.Child=상위 스킬로부터 레벨을 얻음
+Commands.XPGain.Excavation=파헤치기 및 보물 찾기
+Commands.XPGain.Fishing=낚시 (당연한 얘기지요!)
+Commands.XPGain.Herbalism=약초 수확
+Commands.XPGain.Mining=돌 및 광석 채굴
+Commands.XPGain.Repair=수리
+Commands.XPGain.Swords=몬스터 공격
+Commands.XPGain.Taming=동물 조련 또는 늑대와의 전투
+Commands.XPGain.Unarmed=몬스터 공격
+Commands.XPGain.Woodcutting=나무 베기
+Commands.XPGain=&8XP 획득: &f{0}
+Commands.xplock.locked=&6XP 바가 이제 {0}으로 잠겨 있습니다!
+Commands.xplock.unlocked=&6XP 바가 이제 &a잠금 해제&6되었습니다!
+Commands.xprate.modified=&cXP 비율이 {0}(으)로 변경되었습니다
+Commands.xprate.over=&cmcMMO XP 비율 이벤트가 종료되었습니다!!
+Commands.xprate.proper.0=&cXP 비율을 변경하려면 올바른 사용법은 /xprate <정수> 입니다
+Commands.xprate.proper.1=&cXP 비율을 기본값으로 복원하려면 올바른 사용법은 /xprate reset입니다
+Commands.xprate.proper.2=&cXP 이벤트 여부를 지정하려면 true 또는 false를 명시하십시오
+Commands.NegativeNumberWarn=음수를 사용하지 마세요!
+Commands.Event.Start=&amcMMO&6 이벤트!
Commands.Event.Stop=&amcMMO&3 이벤트 종료!
-Commands.Event.Stop.Subtitle=&a즐거운 시간이었기를 바랍니다!
-Commands.Event.XP=&3XP 배율이 이제 &6{0}&3배입니다
+Commands.Event.Stop.Subtitle=&a즐거웠길 바랍니다!
+Commands.Event.XP=&3XP 비율은 이제 &6{0}&3배입니다
Commands.xprate.started.0=&6mcMMO XP 이벤트가 시작되었습니다!
-Commands.xprate.started.1=&6mcMMO XP 배율은 이제 {0}배입니다!
+Commands.xprate.started.1=&6mcMMO XP 비율이 {0}배로 설정되었습니다!
-# Admin Notifications
+# 관리자 알림
Server.ConsoleName=&e[서버]
-Notifications.Admin.XPRate.Start.Self=&7전체 XP 배율을 &6{0}배로 설정했습니다.
-Notifications.Admin.XPRate.End.Self=&7XP 배율 이벤트를 종료했습니다.
-Notifications.Admin.XPRate.End.Others={0} &7님이 XP 배율 이벤트를 종료했습니다.
-Notifications.Admin.XPRate.Start.Others={0} &7님이 전체 XP 배율 {1}배로 이벤트를 시작 또는 수정했습니다.
+Notifications.Admin.XPRate.Start.Self=&7전역 XP 비율 배수를 &6{0}배&7로 설정했습니다
+Notifications.Admin.XPRate.End.Self=&7XP 비율 이벤트를 종료했습니다.
+Notifications.Admin.XPRate.End.Others={0}&7님이 XP 비율 이벤트를 종료했습니다
+Notifications.Admin.XPRate.Start.Others={0}&7님이 전역 배수가 {1}배인 XP 비율 이벤트를 시작하거나 수정했습니다
Notifications.Admin.Format.Others=&6(&amcMMO &3관리자&6) &7{0}
Notifications.Admin.Format.Self=&6(&amcMMO&6) &7{0}
-#GUIDES
-Guides.Available=&7{0} 가이드가 있습니다 - 타입 /{1} ? [페이지]
+# 이벤트
+XPRate.Event=&6mcMMO가 현재 XP 비율 이벤트 중입니다! XP 비율은 {0}배입니다!
+
+#가이드
+Guides.Available=&7{0}에 대한 가이드 사용 가능 - /{1} ? [페이지]
Guides.Header=&6-=&a{0} 가이드&6=-
-Guides.Page.Invalid=올바른 페이지 번호가 아닙니다!
-Guides.Page.OutOfRange=그 페이지는 존재하지 않습니다, 오직 총 {0} 페이지가 있습니다.
-Guides.Usage= 사용법 /{0} ? [페이지]
-
-##Acrobatics
-Guides.Acrobatics.Section.0=&3곡예에 대하여:\n&e곡예는 mcMMO의 우아하게 움직이는 예술입니다.\n&e전투 특혜와 환경 손상 특혜를 증가시킵니다.\n\n&3XP 얻기:\n&e이 스킬의 XP를 얻을려면 전투나 생존에서 피해를 \n&e입는 낙하에서 착지 행동이 요구됩니다.
-Guides.Acrobatics.Section.1=&3어떻게 구르기를 하나요?\n&e당신이 낙하 피해를 받을 때 피해를 무효화할\n&e지속적인 기회를 가지게 됩니다. 웅크리기 키를 누르고 있으면\n&e떨어지는 동안 두 배의 기회를 가지게 됩니다.\n&e이는 일반 구르기 대신 우아한 구르기를 발동시킵니다.\n&e우아한 구르기는 일반 구르기보다 두 배 더 자주 발동되며\n&e일반 구르기보다 더 많은 피해 방어를 제공합니다.\n&e구르기 확률은 스킬 레벨에 연결됩니다.
-Guides.Acrobatics.Section.2=&3어떻게 회피를 하나요?\n&e회피는 당신이 전투에서 상처를 입을 때 입는\n&e피해를 반감시키는 지속적인 기회입니다.\n&e이것은 당신의 스킬 레벨과 연결됩니다.
-
-##Alchemy
-Guides.Alchemy.Section.0=[[DARK_AQUA]]연금술에 대하여:\n[[YELLOW]]연금술은 물약을 양조하는 것입니다.\n[[YELLOW]]물약 양조 시간을 빠르게 하고, 이전에 얻을 수 없었던\n[[YELLOW]]새로운 물약을 추가합니다.\n\n\n[[DARK_AQUA]]XP 획득:\n[[YELLOW]]이 스킬에서 XP를 얻으려면 물약을 양조해야 합니다.
-Guides.Alchemy.Section.1=[[DARK_AQUA]]Catalysis는 어떻게 작동하나요?\n[[YELLOW]]Catalysis는 양조 과정을 가속화시키며, 최대\n[[YELLOW]]속도는 기본 설정에서 레벨 1000에서 4배입니다.\n[[YELLOW]]이 능력은 기본 설정에서 레벨 100에서 잠금 해제됩니다.
-Guides.Alchemy.Section.2=[[DARK_AQUA]]Concoctions는 어떻게 작동하나요?\n[[YELLOW]]Concoctions는 사용자 정의 재료로 더 많은 물약을 양조할 수 있게 합니다.\n[[YELLOW]]잠금 해제되는 특별한 재료는 등급에 따라 결정됩니다.\n[[YELLOW]]잠금 해제할 수 있는 등급은 총 8개입니다.
-Guides.Alchemy.Section.3=[[DARK_AQUA]]Concoctions 1단계 재료:\n[[YELLOW]]블레이즈 가루, 발효된 거미 눈, 가스트 눈물, 레드스톤,\n[[YELLOW]]발광석 가루, 설탕, 반짝이는 수박 조각, 황금 당근,\n[[YELLOW]]마그마 크림, 네더 사마귀, 거미 눈, 수플후르, 워터 릴리,\n[[YELLOW]]복어\n[[YELLOW]](바닐라 물약)
-Guides.Alchemy.Section.4=[[DARK_AQUA]]Concoctions 2단계 재료:\n[[YELLOW]]당근 (신속의 물약)\n[[YELLOW]]슬라임볼 (채굴 피로의 물약)\n\n[[DARK_AQUA]]Concoctions 3단계 재료:\n[[YELLOW]]석영 (흡수의 물약)\n[[YELLOW]]토끼 발 (도약의 물약)
-Guides.Alchemy.Section.5=[[DARK_AQUA]]Concoctions 4단계 재료:\n[[YELLOW]]사과 (생명력 강화의 물약)\n[[YELLOW]]썩은 고기 (허기의 물약)\n\n[[DARK_AQUA]]Concoctions 5단계 재료:\n[[YELLOW]]갈색 버섯 (멀미의 물약)\n[[YELLOW]]잉크 주머니 (실명의 물약)
-Guides.Alchemy.Section.6=[[DARK_AQUA]]Concoctions 6단계 재료:\n[[YELLOW]]고사리 (포화의 물약)\n\n[[DARK_AQUA]]Concoctions 7단계 재료:\n[[YELLOW]]독이 있는 감자 (부패의 물약)\n\n[[DARK_AQUA]]Concoctions 8단계 재료:\n[[YELLOW]]일반 황금 사과 (저항의 물약)
-
-##Archery
-Guides.Archery.Section.0=&3궁술에 대하여:\n&e궁술은 활과 화살로 사격하는 것입니다.\n&e레벨에 따라 증가하는 데미지 보너스와 PvP에서 상대를\n&e혼란시키는 능력과 같은 다양한 전투 보너스를 제공합니다.\n&e또한, 상대의 시체에서 사용한 화살을 일부 회수할 수 있습니다.\n\n\n&3XP 획득:\n&e이 스킬에서 XP를 얻으려면 몹이나 다른 플레이어를\n&e쏴서 맞춰야 합니다.
-Guides.Archery.Section.1=&3Skill Shot은 어떻게 작동하나요?\n&eSkill Shot은 사격에 추가 데미지를 제공합니다.\n&eSkill Shot의 보너스 데미지는 궁술 레벨에 따라\n&e증가합니다.\n&e기본 설정에서 Archery 레벨당 활 데미지가 50 레벨마다\n&e10% 증가하여 최대 200%의 보너스 데미지를 얻을 수 있습니다.
-Guides.Archery.Section.2=&3Daze는 어떻게 작동하나요?\n&e상대를 사격할 때 상대를 혼란시키는 확률이 있습니다.\n&eDaze가 발동되면 상대는 잠시 동안 위를 바라보게 됩니다.\n&eDaze 사격은 추가로 4의 데미지(2 하트)를 입힙니다.
-Guides.Archery.Section.3=&3Arrow Retrieval은 어떻게 작동하나요?\n&e활로 몹을 처치할 때 일부 화살을 회수할 수 있는\n&e확률이 있습니다.\n&e이 확률은 궁술 레벨에 따라 증가합니다.\n&e기본 설정에서 이 능력은 레벨당 0.1%씩 증가하여\n&e레벨 1000에서 100%까지 증가합니다.
-
-##Axes
-Guides.Axes.Section.0=&3부술에 대하여:\n&e부술 스킬을 사용하여 도끼로 나무를 베는 것 이상의 다양한 기능을 사용할 수 있습니다.\n&e부술 스킬을 사용하여 몹과 플레이어를 공격하고 경험치를 얻을 수 있으며,\n&e넉백 효과로 몹에게 치명적인 일격을 가할 수 있습니다.\n&e또한, 레벨이 올라감에 따라 적의 갑옷을 쉽게 파괴할 수 있는\n&e손에 들고 사용하는 나무 굴삭기가 됩니다.\n&3XP 획득:\n&e이 스킬에서 XP를 얻으려면 도끼로 다른 몹이나 플레이어를 공격해야 합니다.
-Guides.Axes.Section.1=&3뼈 쪼개기는 어떻게 작동하나요?\n&e이 능력을 사용하면 AoE(영역 효과) 공격을 할 수 있습니다.\n&e이 AoE 공격은 주요 대상에 가한 데미지의 절반만큼의 데미지를 입힙니다.\n&e따라서 대량의 몹을 제거하는 데에 효과적입니다.
-Guides.Axes.Section.2=&3크리티컬 히트는 어떻게 작동하나요?\n&e크리티컬 히트는 플레이어가 추가 데미지를 입힐 수 있는\n&e확률적인 능력입니다.\n&e기본 설정에서, 부술 스킬 레벨 2마다 0.1%의 확률로\n&e크리티컬 히트를 가할 수 있으며, 이로 인해 몹에게는\n&e2배의 데미지를, 다른 플레이어에게는 1.5배의 데미지를 입힙니다.
-Guides.Axes.Section.3=&3도끼 마스터리는 어떻게 작동하나요?\n&e도끼 마스터리는 액스를 사용할 때 공격에 추가 데미지를\n&e줍니다.\n&e기본 설정에서, 보너스 데미지는 레벨당 50마다 1씩 증가하며,\n&e레벨 200에서 최대 4의 추가 데미지를 얻을 수 있습니다.
-Guides.Axes.Section.4=&3갑옷 충격는 어떻게 작동하나요?\n&e강력한 힘으로 갑옷을 파괴하세요!\n&e갑옷 충격는 상대의 갑옷을 손상시킬 확률이 있습니다.\n&e이 확률은 부술 스킬 레벨이 올라감에 따라 증가합니다.
-Guides.Axes.Section.5=&3엄청난 충격는 어떻게 작동하나요?\n&e부술 스킬로 몹이나 플레이어를 공격할 때\n&e더 큰 영향을 줄 확률이 있습니다.\n&e기본 설정에서 이 확률은 25%입니다.\n&e이 패시브 능력은 넉백 II 마법과 유사한\n&e인챈트 효과를 가지며, 대상에게 추가 데미지를 입힙니다.
-
-##Excavation
-Guides.Excavation.Section.0=&3발굴에 대하여:\n&e발굴은 보물을 찾기 위해 흙을 파내는 행위입니다.\n&e땅을 파내면 보물을 찾을 수 있습니다.\n&e이를 계속하면 더 많은 보물을 찾을 수 있습니다.\n\n&3XP 획득:\n&e이 스킬에서 XP를 얻으려면 삽을 들고 파야 합니다.\n&e일부 재료만 보물과 XP를 얻을 수 있습니다.
-Guides.Excavation.Section.1=&3호환 가능한 재료:\n&e풀, 흙, 모래, 점토, 자갈, 버섯, 영혼 모래, 눈
-Guides.Excavation.Section.2=&3기가 드릴 버서커 사용 방법:\n&e삽을 들고 우클릭하여 도구를 준비합니다.\n&e이 상태에서 약 4초 안에 발굴 호환 가능한 재료에\n&e접촉하면 기가 드릴 버서커가 활성화됩니다.
-Guides.Excavation.Section.3=&3기가 드릴 버서커란 무엇인가요?\n&e기가 드릴 버서커는 발굴 스킬과 연결된 재사용 대기시간이 있는\n&e능력입니다. 이는 보물을 찾을 확률을 세 배로 늘리고\n&e발굴 재료를 즉시 부술 수 있게 합니다.
-Guides.Excavation.Section.4=&3고고학은 어떻게 작동하나요?\n&e발굴을 위한 가능한 모든 보물은 떨어지기 위한\n&e스킬 레벨 요구 사항이 있으므로, 얼마나 도움이 되는지\n&정확히 말하기는 어렵습니다.\n&e단지 기억해 두세요. 발굴 스킬이 높을수록\n&더 많은 보물을 찾을 수 있습니다.\n&또한, 각각의 발굴 호환 가능한 재료에는 고유한 보물 목록이 있습니다.\n&다시 말해, 흙에서 찾는 보물과 자갈에서 찾는 보물은 다릅니다.
-Guides.Excavation.Section.5=&3발굴에 대한 참고 사항:\n&e발굴 보상은 완전히 사용자 정의할 수 있으므로\n&결과는 서버마다 다릅니다.
-
-##Fishing
-Guides.Fishing.Section.0=&3낚시에 대하여:\n&e낚시 스킬이 있다면 낚시가 다시 즐거워집니다!\n&e숨겨진 보물을 찾고 몹에서 아이템을 떨어뜨립니다.\n\n&3XP 획득:\n&e물고기를 낚아서 경험치를 얻습니다.
-Guides.Fishing.Section.1=&3보물 사냥꾼은 어떻게 작동하나요?\n&e이 능력을 사용하면 낚시로 보물을 찾을 수 있습니다.\n&e아이템이 인챈트된 상태로 드롭될 수 있는 작은 확률이 있습니다.\n&e낚시로 얻을 수 있는 모든 보물은 어떤 레벨에서든 드롭될 수 있습니다.\n&e그러나 아이템의 희귀도에 따라 얼마나 자주 드롭되는지가 달라집니다.\n&e낚시 스킬이 높을수록 더 좋은 보물을 찾을 확률이 높아집니다.
-Guides.Fishing.Section.2=&3얼음 낚시는 어떻게 작동하나요?\n&e이 패시브 스킬을 사용하면 얼음 호수에서 낚시를 할 수 있습니다!\n&e낚싯대를 얼음 호수에 던지면 물고기를 낚을 수 있는 작은 구멍이 생성됩니다.
-Guides.Fishing.Section.3=&3낚시꾼 장인은 어떻게 작동하나요?\n&e이 패시브 스킬은 낚시할 때 물고기가 물에 물릴 확률을 증가시킵니다.\n&e이 능력을 잠금 해제하면 보트에서 낚시할 때\n&e물고기를 잡을 확률이 높아집니다.
-Guides.Fishing.Section.4=&3흔들기는 어떻게 작동하나요?\n&e이 액티브 능력을 사용하면 낚싯대로 몹에게서 아이템을 흔들어 떨어뜨릴 수 있습니다.\n&e몹은 일반적으로 죽을 때 떨어뜨리는 아이템을 드롭합니다.\n&e또한, 서바이벌 모드에서는 얻을 수 없는 몹 머리를 획득할 수도 있습니다.
-Guides.Fishing.Section.5=&3어부의 다이어트는 어떻게 작동하나요?\n&e이 패시브 스킬은 물고기를 먹을 때 회복되는 포만감을 증가시킵니다.
-Guides.Fishing.Section.6=&3낚시에 대한 참고 사항:\n&e낚시 아이템은 완전히 사용자 정의할 수 있으므로\n&결과는 서버마다 다릅니다.
-
-##Herbalism
-Guides.Herbalism.Section.0=&3약초학에 대하여:\n&e약초학은 허브와 식물을 수집하는 것에 관한 스킬입니다.\n\n\n&3XP 획득:\n&e식물과 허브를 수집하세요.
-Guides.Herbalism.Section.1=&3호환 가능한 블록:\n&e밀, 감자, 당근, 수박, \n&e호박, 사탕수수, 코코아 콩, 꽃, 선인장, 버섯,\n&e네더 사마귀, 원반, 덩굴.
-Guides.Herbalism.Section.2=&3재배의 대지는 어떻게 작동하나요?\n&e재배의 대지는 액티브 능력으로, 괭이를 들고 우클릭하여\n&e재배의 대지를 활성화할 수 있습니다.\n&e재배의 대지는 식물을 수확할 때 3배의 드롭을 얻을 확률을\n&제공합니다. 또한 인벤토리의 씨앗을 사용하여 블록에\n&생명을 불어넣고 변형시킬 수 있는 능력을 제공합니다.
-Guides.Herbalism.Section.3=&3재배의 재능 (작물)은 어떻게 작동하나요?\n&e이 패시브 능력은 작물을 수확할 때 자동으로 재심을\n&합니다. 성공 확률은 약초학 스킬 레벨에 따라 달라집니다.
-Guides.Herbalism.Section.4=&3재배의 재능 (석재/돌/흙)은 어떻게 작동하나요?\n&e이 액티브 능력은 블록을 해당하는 "식물 관련" 블록으로\n&변환할 수 있게 합니다. 씨앗을 들고 블록을 우클릭하여\n&사용할 수 있습니다. 이 과정에서 1개의 씨앗이 소모됩니다.
-Guides.Herbalism.Section.5=&3농부의 다이어트는 어떻게 작동하나요?\n&e이 패시브 스킬은 빵, 쿠키, 수박, 버섯 스튜, 당근,\n&감자를 섭취할 때 회복되는 포만감을 증가시킵니다.
-Guides.Herbalism.Section.6=&3하이랄인의 행운은 어떻게 작동하나요?\n&e이 패시브 능력은 검으로 특정 블록을 부술 때\n&희귀 아이템을 얻을 확률을 제공합니다.
-Guides.Herbalism.Section.7=&32배 드롭은 어떻게 작동하나요?\n&e이 패시브 능력은 수확 시 더 많은 수확량을 제공합니다.
-
-##Mining
-Guides.Mining.Section.0=&3채광에 대하여:\n&e채광은 돌과 광석을 캐는 것으로, 채굴 시 드롭되는 자원의 양에 보너스를 제공합니다.\n\n&3XP 획득:\n&e이 스킬에서 XP를 얻으려면 손에 곡괭이를 들고 채굴해야 합니다.\n&e일부 블록만 XP를 제공합니다.
-Guides.Mining.Section.1=&3호환 가능한 자료:\n&e돌, 석탄 광석, 철 광석, 금 광석, 다이아몬드 광석, 레드스톤 광석,\n&e청금석 광석, 흑요석, 이끼 낀 석재, 엔더 돌,\n&e발광석, 네더랙입니다.
-Guides.Mining.Section.2=&3파괴자는 어떻게 작동하나요?\n&e곡괭이를 손에 들고, 우클릭을 하면 도구가 준비 상태가 됩니다.\n&e이 상태에서, 4초 안에 채광 가능 블록을 좌클릭하면, 이것은 파괴자를 발동할 것입니다.
-Guides.Mining.Section.3=&3파괴자가 무엇인가요?\n&e파괴자는 채광 스킬과 연결된 재사용 대기시간이 있는 능력입니다. 이는 추가 아이템이 떨어질 확률을 세 배로 늘리고 채광 재료를 즉시 부술 수 있게 합니다.
-Guides.Mining.Section.4=&3폭발 채굴은 어떻게 작동하나요?\n&e곡괭이를 손에 들고 웅크리기를 한 후 TNT를 멀리서 우클릭하세요. 이것은 TNT를 즉시 폭발시킬 것입니다.
-Guides.Mining.Section.5=&3폭발 채굴은 어떻게 작동합니까?\n&e 폭발 채굴은 채광 스킬과 연결된 쿨타임이 있는 기능입니다. TNT로 채굴할 때 보너스를 제공하고 TNT를 원격으로 폭발시킬 수 있습니다. 폭발 채굴에는 세 가지 부분이 있습니다.\n&e 첫 번째 부분은 폭발 반경을 증가시키는 더 큰 폭탄입니다.\n&e 두 번째 부분은 TNT 폭발로 인한 피해를 감소시키는 해체 전문가입니다. 세 번째 부분은 단순히 TNT에서 떨어지는 광석의 양을 증가시키고 떨어지는 파편을 감소시킵니다.
-
-##Repair
-Guides.Repair.Section.0=&3수리에 대하여:\n&e수리는 철 블록을 사용하여 갑옷과 도구를 수리할 수 있게 합니다.\n\n&3XP 획득:\n&emcMMO 모루를 사용하여 도구나 갑옷을 수리하세요. 이는\n&e기본적으로 철 블록이며, Minecraft의 일반 모루와 혼동되지 않아야 합니다.
-Guides.Repair.Section.1=&3어떻게 수리를 사용할 수 있나요?\n&emcMMO 모루를 설치하고 현재 들고 있는 아이템을 우클릭하여 수리하세요. 이는 사용할 때마다 1개의 아이템을 소모합니다.
-Guides.Repair.Section.2=&3수리 마스터리은 어떻게 작동하나요?\n&e수리 마스터리은 수리량을 증가시킵니다. 추가로 수리되는 양은 수리 스킬 레벨에 영향을 받습니다.
-Guides.Repair.Section.3=&3슈퍼 수리는 어떻게 작동하나요?\n&e슈퍼 수리는 패시브 능력입니다. 아이템을 수리할 때\n&e더욱 효과적으로 아이템을 수리할 수 있는 기회를 제공합니다.
-Guides.Repair.Section.4=&3인챈트 아이템 수리는 어떻게 작동하나요?\n&e이 패시브 능력은 일정 확률로 아이템을 수리할 때\n&e인챈트를 유지할 수 있게 합니다. 인챈트는\n&e기존 레벨로 유지되거나 낮은 레벨로 강등되거나\n&완전히 사라질 수 있습니다.
-
-##Salvage
-Guides.Salvage.Section.0=&3회수에 대하여:\n&e회수는 금 블록을 사용하여 갑옷과 도구를 회수할 수 있게 합니다.\n\n&3XP 획득:\n&e회수는 수리 및 낚시의 부가 스킬로, 회수 스킬 레벨은 낚시 및 수리 스킬 레벨에 기반합니다.
-Guides.Salvage.Section.1=&3회수를 어떻게 사용할 수 있나요?\n&emcMMO 회수 모루를 설치하고 현재 들고 있는 아이템을 우클릭하여 회수하세요. 이렇게 하면 아이템이 분해되고 아이템을 제작하는 데 사용된 재료가 반환됩니다.\n\n&e예를 들어, 철 곡괭이를 회수하면 철 주괴를 얻을 수 있습니다.
-Guides.Salvage.Section.2=&3전문적인 회수는 어떻게 작동하나요?\n&e전문적인 회수를 잠금 해제하면 손상된 아이템을 회수할 수 있습니다. 레벨이 올라감에 따라 수확률이 증가합니다. 높은 수확률은 더 많은 재료를 얻을 수 있음을 의미합니다.\n&e전문적인 회수를 사용하면 항상 1개의 재료를 얻게 되며, 아이템이 너무 손상된 경우를 제외하고는 아이템을 파괴하고 아무것도 얻지 못하는 일은 없습니다.
-Guides.Salvage.Section.3=&3작동 방식을 설명하기 위해 예를 들어보겠습니다:\n&e손상된 금 곡괭이를 회수한다고 가정해 봅시다. 이 경우 최대로 얻을 수 있는 양은 2개입니다(곡괭이는 3개의 주괴로 제작되며 각각의 주괴는 33.33%의 내구성을 가지므로 66%에 해당하는 2개입니다). 수확률이 66%보다 낮으면 2개의 주괴를 얻을 수 없습니다. 수확률이 이 값보다 높으면 "전체 양"을 얻을 수 있으며, 즉 2개의 주괴를 얻게 됩니다.
-Guides.Salvage.Section.4=&3신비로운 회수는 어떻게 작동하나요?\n&e이 능력을 사용하면 마법이 부여된 아이템을 회수할 때 마법이 부여된 책을 얻을 수 있습니다. 레벨에 따라 완전한 부분 또는 부분적인 부여를 성공적으로 추출할 확률이 다릅니다.\n\n&e부분적으로 추출된 경우, 부여된 책은 아이템에 있던 부여보다 낮은 레벨의 부여를 가지게 됩니다.
-
-##Smelting
-Guides.Smelting.Section.0=준비 중...
-
-##Swords
-Guides.Swords.Section.0=&3검술에 대해:\n&e이 스킬은 검을 사용하는 사람에게 전투 보너스를 제공합니다.\n\n&3XP 획득:\n&e검을 사용하여 몹이나 다른 플레이어에게 입힌 데미지에 따라 경험치를 획득합니다.
-Guides.Swords.Section.1=&3톱날 공격은 어떻게 작동하나요?\n&e톱날 공격은 액티브 능력으로, 검으로 우클릭하여 활성화할 수 있습니다. 이 능력을 사용하면 AoE(영역 효과) 공격을 할 수 있습니다. 이 AoE는 추가 25%의 데미지를 입히며, Rupture를 적용할 수도 있습니다.
-Guides.Swords.Section.2=&3카운터 어택은 활성 능력입니다. 몹으로부터 공격을 막으면서 피해를 입을 때, 받은 피해의 50%를 반사할 확률이 있습니다.
-Guides.Swords.Section.3=&3파열은 적에게 2초마다 피해를 입힙니다. 이 효과는 피해를 입은 대상이 죽거나 효과가 사라질 때까지 계속됩니다. 검 기술이 높을수록 피 효과의 지속 시간이 증가합니다.
-
-##Taming
-Guides.Taming.Section.0=&3조련에 대하여:\n&e조련은 길들인 늑대를 사용할 때 다양한 전투 보너스를 제공합니다.\n\n&3XP 획득:\n&e이 스킬에서 경험치를 얻으려면 늑대/오셀롯을 길들이거나\n&e늑대와 전투해야 합니다.
-Guides.Taming.Section.1=&3야생의 포효는 어떻게 작동하나요?\n&e야생의 포효는 액티브 능력으로, 뼈다귀나 생선을 들고\n&e웅크리고 좌클릭하여 늑대나 오셀롯을 소환할 수 있습니다.
-Guides.Taming.Section.2=&3짐승의 포효는 어떻게 작동하나요?\n&e짐승의 포효는 펫을 검사하고 늑대와 오셀롯의\n&estats를 확인할 수 있게 합니다. 늑대나 오셀롯을 좌클릭하여\n&e짐승의 포효를 사용하세요.
-Guides.Taming.Section.3=&3돌진은 어떻게 작동하나요?\n&e돌진은 패시브 능력으로, 늑대의 대상에게 출혈 효과를\n&적용할 수 있는 확률이 있습니다.
-Guides.Taming.Section.4=&3날카로운 발톱은 어떻게 작동하나요?\n&e날카로운 발톱은 늑대가 입히는 피해에 추가적인 데미지 보너스를\n&제공합니다. 이 데미지 보너스는 테이밍 레벨에 따라 달라집니다.
-Guides.Taming.Section.5=&3환경 인식은 어떻게 작동하나요?\n&e이 패시브 능력은 늑대가 선인장/용암과 같은 위험 요소에\n&가까이 다가갈 때 당신에게 순간이동할 수 있게 합니다.\n&또한, 늑대에게 낙하 피해 면역성을 제공합니다.
-Guides.Taming.Section.6=&3두꺼운 털은 어떻게 작동하나요?\n&e이 패시브 능력은 늑대의 피해를 감소시키고\n&불에 대한 내성을 제공합니다.
-Guides.Taming.Section.7=&3충격 방지는 어떻게 작동하나요?\n&e이 패시브 능력은 늑대가 폭발로 인한 피해를\n&감소시킵니다.
-Guides.Taming.Section.8=&3빠른 음식 제공은 어떻게 작동하나요?\n&e이 패시브 능력은 늑대가 공격할 때마다 회복할\n&수 있는 기회를 제공합니다.
-
-##Unarmed
-Guides.Unarmed.Section.0=&3비무장에 대해:\n&e비무장은 주먹을 무기로 사용할 때 플레이어에게 다양한 전투 보너스를 제공합니다. \n\n&3XP 획득:\n&e맨손으로 몹이나 다른 플레이어에게 입힌 피해량에 따라 경험치를 획득합니다.
-Guides.Unarmed.Section.1=&3버서커는 어떻게 작동하나요?\n&e버서커는 우클릭으로 활성화되는 액티브 능력입니다. 버서커 모드에서는 추가로 50%의 피해를 입히며, 흙과 풀과 같은 약한 재료를 즉시 부술 수 있습니다.
-Guides.Unarmed.Section.2=&3강철 팔 형태는 어떻게 작동하나요?\n&e강철 팔 형태는 주먹으로 몹이나 플레이어를 때릴 때 입히는 피해량을 증가시킵니다.
-Guides.Unarmed.Section.3=&3화살 회피는 어떻게 작동하나요?\n&e화살 튕김은 스켈레톤이나 다른 플레이어가 발사한 화살을 튕길 확률을 제공하는 패시브 능력입니다. 화살은 피해를 입히지 않고 땅에 떨어집니다.
-Guides.Unarmed.Section.4=&3강철 주먹은 어떻게 작동하나요?\n&e강철 주먹은 다른 스킬의 강제 무장 해제 효과를 방지하는 패시브 능력입니다. 비무장 레벨이 올라갈수록 강제 무장 해제를 방지하는 확률이 증가합니다.
-Guides.Unarmed.Section.5=&3비무장은 어떻게 작동하나요?\n&e이 패시브 능력은 플레이어가 다른 플레이어의 무기를 해제할 수 있게 해줍니다. 대상의 장착된 아이템은 땅에 떨어집니다.
-
-##Woodcutting
-Guides.Woodcutting.Section.0=&3벌목에 대하여:\n&e벌목은 나무를 베는 것에 관한 스킬입니다.\n\n&3XP 획득:\n&e나무 블록을 부술 때마다 경험치를 얻습니다.
-Guides.Woodcutting.Section.1=&3나무꾼은 어떻게 작동하나요?\n&e나무꾼은 액티브 능력으로, 도끼를 들고 우클릭하여\n&e나무 베기를 활성화할 수 있습니다. 이렇게 하면\n&e전체 나무가 즉시 부서지고 한 번에 모든\n&나무 블록이 떨어집니다.
-Guides.Woodcutting.Section.2=&3나뭇잎 청소는 어떻게 작동하나요?\n&e나뭇잎 청소는 패시브 능력으로, 도끼로\n&맞힌 잎사귀 블록이 즉시 부서지게 합니다.\n&기본적으로 이 능력은 레벨 100에서 해금됩니다.
-Guides.Woodcutting.Section.3=&3드롭 2배는 어떻게 작동하나요?\n&e이 패시브 능력은 베는 나무마다 추가로\n&한 개의 블록을 얻을 수 있는 기회를 제공합니다.
-
-#INSPECT
-Inspect.Offline= &c그 플레이어는 오프라인 상태이므로 검사할 수 없습니다. 오프라인 플레이어를 검사하려면 권한이 필요합니다.
-Inspect.OfflineStats=mcMMO 오프라인 유저 스텟은 &e{0} 입니다
-Inspect.Stats=&amcMMO 스텟은 &e{0} 입니다
-Inspect.TooFar=당신은 그 플레이어와 너무 멀리 떨어져 있어 검사할 수 없습니다!
-
-#ITEMS
-Item.ChimaeraWing.Fail=**키메라의 날개 실패!**
-Item.ChimaeraWing.Pass=**키메라의 날개**
-Item.ChimaeraWing.Name=키메라의 날개
-Item.ChimaeraWing.Lore=&7당신의 침대로 텔레포트합니다.
-Item.Generic.Wait=키메라의 날개를 다시 사용하려면 &e({0}초) &c기다려야 합니다!
-Item.Injured.Wait=당신은 최근에 부상을 당했으며 이것을 사용하려면 &e({0}초) &f기다려야 합니다
-Item.FluxPickaxe.Name=용해 곡괭이
-Item.FluxPickaxe.Lore.1=&7광물을 즉시 제련할 기회를 가집니다.
-Item.FluxPickaxe.Lore.2=&7제련 요구 레벨 {0} 이상
-
-#TELEPORTATION
-Teleport.Commencing=&7텔레포트가 &6({0}) &7초안에 시작됩니다, 가만히 기다려주세요...
+Guides.Page.Invalid=유효한 페이지 번호가 아닙니다!
+Guides.Page.OutOfRange=해당 페이지가 존재하지 않습니다. 전체 페이지 수는 {0}입니다.
+Guides.Usage= 사용법: /{0} ? [페이지]
+##곡예
+Guides.Acrobatics.Section.0=&3곡예에 대해:\n&e곡예는 mcMMO에서 우아하게 움직이는 예술입니다.\n&e전투 보너스 및 환경 피해 보너스를 제공합니다.\n\n&3XP 획득:\n&e이 스킬에서 XP를 획득하려면 전투에서 피격을 회피하거나\n&e높은 곳에서 떨어져서 피해를 입어야 합니다.
+Guides.Acrobatics.Section.1=&3롤링이 작동하는 방법은?\n&e낙하 피해를 입을 때 자동으로 피해를 무효화할\n&epassive한 확률이 있습니다.\n&eeSneak 버튼을 누르고 낙하 중에 확률을 두배로 늘릴 수 있습니다.\n&e이로 인해 표준 롤이 아닌 우아한 롤이 발생합니다.\n&e우아한 롤은 일반 롤과 유사하지만 두 배의 확률로\n&e발생하며 표준 롤보다 더 많은 피해 안전을 제공합니다.\n&e롤링 확률은 스킬 레벨에 결합됩니다.
+Guides.Acrobatics.Section.2=&3회피가 작동하는 방법은?\n&e회피는 전투 중에\n&e입은 피해를 절반으로 줄이는 자동 확률입니다.\n&e스킬 레벨에 결합됩니다.
+##연금술
+Guides.Alchemy.Section.0=[[DARK_AQUA]]연금술에 대해:\n[[YELLOW]]연금술는 포션 제조와 관련이 있습니다.\n[[YELLOW]]포션 제조 시간을 빠르게 할 뿐만 아니라\n[[YELLOW]]이전에 얻을 수 없던 새로운 포션도 추가합니다.\n\n\n[[DARK_AQUA]]XP 획득:\n[[YELLOW]]이 스킬에서 XP를 획득하려면 포션을 제조해야 합니다.
+Guides.Alchemy.Section.1=[[DARK_AQUA]]카탈리시스가 작동하는 방법은?\n[[YELLOW]]카탈리시스는 제조 과정을 가속화합니다.\n[[YELLOW]]레벨 100에서 최대 속도가 4배까지 증가합니다.\n[[YELLOW]]이 능력은 기본적으로 레벨 100에서 잠금 해제됩니다.
+Guides.Alchemy.Section.2=[[DARK_AQUA]]조제가 작동하는 방법은?\n[[YELLOW]]조제는 사용자 정의 재료로 더 많은 포션을 제조할 수 있습니다.\n[[YELLOW]]잠금 해제된 특별한 재료는 사용자의 등급에 따라 결정됩니다.\n[[YELLOW]]잠금 해제된 특별한 재료는 사용자의 등급에 따라 결정됩니다. 8개의 등급이 있습니다.
+Guides.Alchemy.Section.3=[[DARK_AQUA]]조제 1단계 재료:\n[[YELLOW]]Blaze Powder, Fermented Spider Eye, Ghast Tear, Redstone,\n[[YELLOW]]Glowstone Dust, Sugar, Glistering Melon, Golden Carrot,\n[[YELLOW]]Magma Cream, Nether Wart, Spider Eye, Suplhur, Water Lily,\n[[YELLOW]]Pufferfish\n[[YELLOW]](기본 포션)
+Guides.Alchemy.Section.4=[[DARK_AQUA]]조제 2단계 재료:\n[[YELLOW]]Carrot (Potion of Haste)\n[[YELLOW]]Slimeball (Potion of Dullness)\n\n[[DARK_AQUA]]조제 3단계 재료:\n[[YELLOW]]Quartz (Potion of Absorption)\n[[YELLOW]]Rabbit's Foot (Potion of Leaping)
+Guides.Alchemy.Section.5=[[DARK_AQUA]]조제 4단계 재료:\n[[YELLOW]]Apple (Potion of Health Boost)\n[[YELLOW]]Rotten Flesh (Potion of Hunger)\n\n[[DARK_AQUA]]조제 5단계 재료:\n[[YELLOW]]Brown Mushroom (Potion of Nausea)\n[[YELLOW]]Ink Sack (Potion of Blindness)
+Guides.Alchemy.Section.6=[[DARK_AQUA]]조제 6단계 재료:\n[[YELLOW]]Fern (Potion of Saturation)\n\n[[DARK_AQUA]]조제 7단계 재료:\n[[YELLOW]]Poisonous Potato (Potion of Decay)\n\n[[DARK_AQUA]]조제 8단계 재료:\n[[YELLOW]]Regular Golden Apple (Potion of Resistance)
+#양궁
+Guides.Archery.Section.0=&3양궁에 대해:\n&e양궁는 활과 화살로 사격하는 것에 관한 것입니다.\n&e전투 보너스로는 레벨에 따라 증가하는 추가 데미지\n&ee와 PvP에서 상대를 헷갈리게 하는 능력 등이 있습니다.\n&ee이외에도 적들의 시체에서 사용한 화살 중 일부를\n&ee회수할 수 있습니다.\n\n\n&3XP 획득:\n&e이 스킬에서 XP를 획득하려면 몹이나 다른 플레이어를\n&ee사격해야 합니다.
+Guides.Archery.Section.1=&3스킬샷이 작동하는 방법은?\n&e스킬샷은 사격에 추가 데미지를 제공합니다.\n&e스킬샷의 보너스 데미지는 양궁 레벨이\n&ee증가함에 따라 증가합니다.\n&e기본 설정에서 양궁 데미지는 매 50 레벨마다\n&ee 10%씩 증가하여 최대 200%의 보너스 데미지를 얻습니다.
+Guides.Archery.Section.2=&3현혹이 작동하는 방법은?\n&e상대를 사격할 때 다른 플레이어를 헷갈리게 할\n&e자동 확률이 있습니다. 현혹이 발생하면 상대방을\n&ekort 올려 상당한 시간 동안 움직일 수 없게 합니다.\n&e현훅 사격은 추가적으로 4 데미지(2 하트)를 줍니다.
+Guides.Archery.Section.3=&3화살 회수가 작동하는 방법은?\n&e활로 몹을 죽일 때 사용한 화살 중 일부를\n&e회수할 수 있는 자동 확률이 있습니다.\n&e양궁 레벨이 증가함에 따라 이 확률이\n&ee증가합니다.\n&e기본적으로 레벨마다 0.1%씩 증가하여 레벨 1000에서\n&ee 100%까지 증가합니다.
+##참수
+Guides.Axes.Section.0=&3참수에 대해:\n&eAxe 스킬을 사용하면 나무를 베는 것 이상의 일에\n&e도끼를 사용할 수 있습니다! 몹과 플레이어를 찍고\n&e베어 경험치를 얻을 수 있습니다. 다른 몹에게\n&e넉백 효과를 적용하고 몹과 플레이어에게 치명적인\n&e크리티컬을 입힐 수도 있습니다. 레벨이 오르면\n&ee아처의 갑옷을 쉽게 깨는 수동 핸드 헬드 우드칩퍼가 됩니다.\n&3XP 획득:\n&e이 스킬에서 XP를 획득하려면 도끼로 다른 몹이나 플레이어를\n&eb 치면 됩니다.
+Guides.Axes.Section.1=&3스컬 스플리터가 작동하는 방법은?\n&e이 능력은 범위 피해를 입히도록 허용합니다.\n&ee이 범위 피해는 주요 대상에 가한 피해의 절반이 됩니다.\n&e그래서 많은 몹을 쉽게 제거할 수 있습니다.
+Guides.Axes.Section.2=&3치명타가 작동하는 방법은?\n&e치명타는 추가 데미지를 줄 수 있는\n&e확률을 제공하는 수동 능력입니다.\n&e기본 설정에서 Axes 2 레벨마다 0.1%의 기회를\n&e얻으며, 이로 인해 몹에게는 2배의 데미지,\n&e플레이어에게는 1.5배의 데미지를 입힐 수 있습니다.
+Guides.Axes.Section.3=&3도끼 마스터리가 작동하는 방법은?\n&e도끼 마스터리는 도끼를 사용할 때 피해를 추가합니다.\n&e기본적으로 50 레벨마다 보너스 데미지가 1씩\n&e증가하여 레벨 200에서 최대 4의 추가 피해를\n&ee받습니다.
+Guides.Axes.Section.4=&3갑옷 충격이 작동하는 방법은?\n&e갑옷을 파괴할만큼 충격적으로 공격하세요!\n&e갑옷 충격에는 상대의 갑옷을 손상시킬 수 있는\n&ee자동 확률이 있습니다. Axes 레벨이 오르면\n&ee이 데미지가 증가합니다.
+Guides.Axes.Section.5=&3더 큰 영향이 작동하는 방법은?\n&e도끼로 몹이나 플레이어를 공격할 때 더 큰\n&e영향을 줄 수 있는 자동 확률이 있습니다.\n&e기본적으로 이 확률은 25%입니다. 이 자동 능력은\n&e넉백 II 인첸트와 유사한 극단적인 넉백 효과를\n&ee가지며 대상에게 추가 데미지를 줍니다.
+##발굴
+Guides.Excavation.Section.0=&3발굴에 대해:\n&e발굴은 보물을 찾기 위해 흙을 파는 작업입니다.\n&e땅을 파면 보물을 찾을 수 있습니다.\n&e이를 통해 더 많은 보물을 찾을 수 있습니다.\n\n&3XP 획득:\n&e이 스킬에서 XP를 획득하려면 손에 삽을 들고 파야 합니다.\n&e특정한 재료만이 보물과 XP를 얻을 수 있습니다.
+Guides.Excavation.Section.1=&3호환되는 재료:\n&e풀, 흙, 모래, 점토, 자갈, 버섯, 영혼 모래, 눈
+Guides.Excavation.Section.2=&3기가 드릴 브레이커 사용 방법:\n&e삽을 손에 든 상태에서 우클릭하여 도구를 준비합니다.\n&e이 상태에서 약 4초 동안 발굴 호환 재료에\n&e접촉하면 기가 드릴 브레이커를 활성화합니다.
+Guides.Excavation.Section.3=&3기가 드릴 브레이커가 무엇인가요?\n&e기가 드릴 브레이커는 발굴 스킬에 링크된\n&e재사용 대기시간이 있는 능력입니다. 보물을 찾을 기회를\n&e3배로 늘리고 발굴 재료에 대한 즉시 부서를\n&ee능력화합니다.
+Guides.Excavation.Section.4=&3고고학이 작동하는 방법은?\n&e발굴을 위한 모든 가능한 보물은 획득을 위한\n&e스킬 레벨 요구 사항이 있기 때문에\n&e어느 정도 도움이 되는지 어렵습니다.\n&e단지 발굴 스킬이 높을수록 찾을 수 있는\n&ee보물이 많아집니다.\n&e또한 각 발굴 호환 재료 유형마다 고유한\n&e보물 목록이 있습니다.\n&e다시 말해 Dirt에서는 다른 보물을 발견합니다.\n&ee Gravel.
+Guides.Excavation.Section.5=&3발굴에 대한 참고 사항:\n&e발굴 드롭은 완전히 사용자 정의할 수 있습니다.\n&e결과는 서버마다 다릅니다.
+##낚시
+Guides.Fishing.Section.0=&3낚시에 관하여:\n&e낚시 스킬을 사용하면 낚시가 다시 흥미로워집니다!\n&e숨겨진 보물을 발견하고 몹에서 아이템을 떨어뜨릴 수 있습니다.\n\n&3XP 획득:\n&e물고기를 낚아라.
+Guides.Fishing.Section.1=&3보물 사냥꾼가 작동하는 방법은?\n&e이 능력을 통해 낚시로부터 보물을 발견할 수 있으며\n&e아이템의 일부가 마법부여되어 있는 작은 확률이 있습니다.\n&e낚시의 모든 가능한 보물은 어느 레벨에서나 떨어질 수 있는\n&e확률이 있습니다. 그러나 아이템의 희귀도에 따라\n&ee얼마나 자주 떨어지는지가 달라집니다.\n&e낚시 스킬이 높을수록 더 좋은 보물을 발견할 확률이\n&ee더 좋아집니다.
+Guides.Fishing.Section.2=&3얼음 낚시이 작동하는 방법은?\n&e이 수동 능력을 통해 얼음 호수에서 낚시할 수 있습니다!\n&e얼음 호수에 낚싯대를 던지면 능력이 활성화되어\n&e작은 구멍을 만들어 낚시를 할 수 있습니다.
+Guides.Fishing.Section.3=&3낚시꾼의 대가가 작동하는 방법은?\n&e이 수동 능력은 낚시할 때 물고기가 낚일 확률을\n&e증가시킵니다. 이 능력을 해제하면 보트에 탑승한\n&ee상태에서 낚시할 때 물고기를 잡을 확률이 높아집니다.
+Guides.Fishing.Section.4=&3떨림이 작동하는 방법은?\n&e이 수동 능력을 사용하면 낚싯대로 몹에게서 아이템을\n&e떨어뜨릴 수 있습니다. \n&e몹은 일반적으로 죽을 때 떨어뜨릴 아이템을 떨어뜨립니다.\n&e생존 모드에서 획득할 수 없는 몹 해골도 획득할 수 있습니다.
+Guides.Fishing.Section.5=&3어부의 다이어트가 작동하는 방법은?\n&e이 수동 능력은 물고기를 먹을 때 회복되는\n&e배고픔의 양을 증가시킵니다.
+Guides.Fishing.Section.6=&3낚시에 관한 참고 사항:\n&e낚시 드롭은 완전히 사용자 정의할 수 있으므로,\n&ee결과는 서버마다 다릅니다.
+##약초학
+Guides.Herbalism.Section.0=&3허브 수집에 관하여:\n&eHerbalism은 허브와 식물을 수집하는 것입니다.\n\n\n&3XP 획득:\n&e식물과 허브를 수집하십시오.
+Guides.Herbalism.Section.1=&3호환되는 블록\n&e밀, 감자, 당근, 수박,\n&e호박, 사탕수수, 코코아, 꽃, 선인장, 버섯,\n&e네더 와트, 연꽃잎, 덩굴.
+Guides.Herbalism.Section.2=&3녹색 에너지가 작동하는 방법은?\n&e녹색 에너지는 활성 능력으로, 괭이를 들고 있는\n&ee상태에서 마우스 오른쪽 버튼을 클릭하여 녹색 에너지를\n&e활성화할 수 있습니다.\n&e녹색 에너지는 플레이어에게 작물을 수확할 때 3배의 드롭을\n&ee얻을 기회를 부여합니다. 또한 인벤토리에서 씨앗을 사용하여\n&e블록에 생명을 주고 변형시킬 수 있는 능력을 제공합니다.
+Guides.Herbalism.Section.3=&3식물 재배 (Crops)가 작동하는 방법은?\n&e이 수동 능력은 수확할 때 자동으로 작물을 재배합니다.\n&ee성공 확률은 허브 스킬에 따라 다릅니다.
+Guides.Herbalism.Section.4=&3식물 재배 (Cobble/Stone Brick/Dirt)가 작동하는 방법은?\n&e이 수동 능력을 사용하면 블록을 "식물 관련" 동료로 변환할 수 있습니다.\n&e씨앗을 들고 블록을 마우스 오른쪽 버튼으로 클릭하면 됩니다. 이렇게 하면 1개의 씨앗이 소비됩니다.
+Guides.Herbalism.Section.5=&3Farmer's Diet가 작동하는 방법은?\n&e이 수동 능력은 빵, 쿠키, 수박, 버섯 수프, 당근,\n&ee감자를 먹을 때 회복되는 배고픔의 양을 증가시킵니다.
+Guides.Herbalism.Section.6=&3약초학의 행운이 작동하는 방법은?\n&e이 수동 능력은 일부 블록이 검술로 파괴될 때 희귀 아이템을 찾을 기회를 부여합니다.
+Guides.Herbalism.Section.7=&3더블 드롭이 작동하는 방법은?\n&e이 수동 능력은 수확할 때 플레이어에게 더 많은 수확량을 제공합니다.
+##채광
+Guides.Mining.Section.0=&3광업에 관하여:\n&e광업은 돌과 광물을 캐는 것으로, 채굴할 때 드롭되는 자원의 양을\n&e증가시켜줍니다.\n\n&3XP 획득:\n&e이 스킬에서 XP를 얻으려면 손에 곡괭이를 들고 채굴해야 합니다.\n&e일부 블록들만이 XP를 부여합니다.
+Guides.Mining.Section.1=&3호환되는 블록:\n&e돌, 석탄 광석, 철 광석, 금 광석, 다이아몬드 광석, 레드스톤 광석,\n&e청금석 광석, 옵시디언, 이끼 난간돌, 엔더 돌,\n&e발광석, 네더랙.
+Guides.Mining.Section.2=&3슈퍼 브레이커를 사용하는 방법은?\n&e손에 곡괭이를 들고 마우스 오른쪽 버튼을 클릭하여 도구를\n&e준비합니다. 이 상태에서 광업 호환되는 블록에 접촉하여\n&e슈퍼 브레이커를 활성화합니다.
+Guides.Mining.Section.3=&3슈퍼 브레이커란 무엇인가요?\n&e슈퍼 브레이커는 광업 스킬에 결합된 쿨다운이 있는 능력입니다.\n&e추가 아이템이 드롭되는 확률을 세 배로 증가시키고,\n&e광업 블록에 대해 즉시 파괴할 수 있습니다.
+Guides.Mining.Section.4=&3폭파 광업을 사용하는 방법은?\n&e손에 곡괭이를 들고,\n&e웅크리고 TNT를 거리에서 마우스 오른쪽 버튼으로 클릭합니다.\n&e그러면 TNT가 즉시 폭발합니다.
+Guides.Mining.Section.5=&3폭파 광업이 작동하는 방법은?\n&e폭파 광업은 광업 스킬에 결합된 쿨다운이 있는 능력입니다.\n&eTNT와 함께 채굴할 때 보너스를 제공하고,\n&eTNT를 원격으로 폭파할 수 있습니다. 폭파 광업에는 세 가지 부분이 있습니다.\n&e첫 번째는 Bigger Bombs로, 폭발 반경을 증가시킵니다.\n&e두 번째는 철거 전문가로, TNT 폭발로부터의\n&e피해를 감소시킵니다. 세 번째 부분은 단순히\n&eTNT로부터 드롭되는 광석의 양을 증가시키고\n&e파편을 감소시킵니다.
+##수리하다
+Guides.Repair.Section.0=&3수리에 관하여:\n&e수리를 사용하면 철 블록을 사용하여 갑옷과\n&e도구를 수리할 수 있습니다.\n\n&3XP 획득:\n&emcMMO 모루를 사용하여 도구나 갑옷을 수리하세요.\n&edefault로 철 블록이며, Vanilla Minecraft 모루와\n&e혼동해서는 안 됩니다.
+Guides.Repair.Section.1=&3어떻게 수리를 사용할 수 있나요?\n&emcMMO 모루를 설치하고 현재 들고 있는 아이템을 수리하려면\n&emcMMO 모루를 클릭합니다. 이렇게 하면 사용할 때마다 1개의 아이템이 소모됩니다.
+Guides.Repair.Section.2=&3수리 마스터리가 작동하는 방법은?\n&e수리 마스터리는 수리 양을 증가시킵니다. 추가로 수리된\n&e양은 수리 스킬 레벨에 영향을 받습니다.
+Guides.Repair.Section.3=&3슈퍼 수리가 작동하는 방법은?\n&e슈퍼 수리는 수동 능력입니다. 아이템을 수리할 때\n&ee더 효과적으로 아이템을 수리할 기회를 부여합니다.
+Guides.Repair.Section.4=&3비밀의 단조가 작동하는 방법은?\n&e이 수동 능력을 사용하면 특정 확률로 인챈트된 아이템을 수리하고\n&ee던지지 않을 수 있습니다. 인챈트는 현재 레벨에 따라\n&ee그대로 유지되거나 낮은 수준으로 강등되거나\n&ee완전히 손실될 수 있습니다.
+##산출기능술
+Guides.Salvage.Section.0=&3산출에 관하여:\n&e산출을 사용하면 금 블록을 사용하여 갑옷과\n&e도구를 분해할 수 있습니다.\n\n&3XP 획득:\n&e산출은 수리 및 어업의 하위 스킬이며, 여러분의 산출\n&eskil 레벨은 여러분의 낚시 및 수리 스킬 레벨에 기반합니다.
+Guides.Salvage.Section.1=&3어떻게 산출을 사용할 수 있나요?\n&emcMMO 산출 모루를 설치하고 현재 들고 있는 아이템을\n&emcMMO 산출 모루를 클릭합니다. 이렇게 하면 아이템이 분해되고,\n&e아이템을 제작하는 데 사용된 자원을 돌려받습니다.\n\n&예를 들어, 철 곡괭이를 산출하면 철 막대를 얻게 됩니다.
+Guides.Salvage.Section.2=&3고급 산출이 작동하는 방법은?\n&e이 능력을 잠금 해제하면 손상된 아이템을 분해할 수 있습니다.\n&e생산 비율은 레벨이 올라감에 따라 증가합니다. 높은 생산률은\n&ee더 많은 자원을 얻을 수 있다는 의미입니다.\n&e고급 산출로 인해 항상 1개의 자원을 얻게 되며,\n&e아이템이 너무 손상되어 있는 경우에는 아무것도 얻지 못합니다.\n&e부스러기를 드롭하지 않고.
+Guides.Salvage.Section.3=&3이를 설명하기 위해 예를 들어보겠습니다.\n&ee.g. 우리가 20% 손상된 금 곡괭이를 산출한다면,\n&ee최대 얻을 수 있는 양은 2개입니다.\n&e(곡괭이는 각각 33.33% 내구도로 제작되었으므로 각각 66%를 나타냅니다.)\n&ee당신의 수익률이 66% 미만인 경우 2개의 막대를 얻을 수 없습니다.\n&ee그 이상인 경우 "전체 양"을 얻을 수 있습니다.\n&ee이는 2개의 막대를 얻게됩니다.
+Guides.Salvage.Section.4=&3비밀의 산출이 작동하는 방법은?\n&e이 능력을 사용하면 인챈트된 아이템을 산출하여 인챈트된 책을\n&ee얻을 수 있습니다. 성공적으로 전체 또는 부분적인 인챈트를\n&ee추출할 확률은 여러분의 레벨에 따라 다릅니다.\n\n&ee인챈트가 부분적으로 추출되는 경우 인챈트된\n&ee책은 아이템에 있던 것보다 낮은 레벨의 인챈트를 가지게 됩니다.
+##제련
+Guides.Smelting.Section.0=곧 출시됩니다...
+##검
+Guides.Swords.Section.0=&3검에 관하여:\n&e이 스킬은 검을 사용하는 모든 사람에게 전투 보너스를 부여합니다.\n\n&3XP 획득:\n&e검을 사용하여 몹이나 다른 플레이어에게 입힌 데미지 양에 따라 XP를 획득합니다.
+Guides.Swords.Section.1=&3톱날 타격이 작동하는 방법은?\n&e톱날 타격은 액티브 능력으로, 검을 들고 우클릭하여 활성화할 수 있습니다.\n&ee오브리오트 (효과 범위) 타격을 할 수 있습니다. 이 AO는\n&e보너스 25% 데미지를 가하고 럽처를 적용할 수 있습니다.
+Guides.Swords.Section.2=&3반격이 작동하는 방법은?\n&e반격은 액티브 능력입니다. 몹으로부터 공격을 받을 때 블록하면\n&ee받은 데미지의 50%를 반사할 기회가 생깁니다.
+Guides.Swords.Section.3=&3파열이 작동하는 방법은?\n&e파열은 적이 매 두 초마다 피해를 입게 만듭니다. 효과가 사라지거나\n&ee적이 죽을 때까지 타겟은 출혈합니다.\n&e출혈 지속 시간은 검 스킬에 따라 증가합니다.
+##길들이기
+Guides.Taming.Section.0=&3테이밍에 관하여:\n&e테이밍은 길들인 늑대를 사용할 때 플레이어에게 다양한 전투 보너스를 부여합니다.\n\n&3XP 획득:\n&e이 스킬에서 XP를 얻으려면 늑대/오셀롯을 길들이거나\n&e자신의 늑대와 전투에 들어가야 합니다.
+Guides.Taming.Section.1=&3야생의 부름이 작동하는 방법은?\n&e야생의 부름은 능동적인 능력으로, 뼈나 생선을 들고 있을 때 웅크리고 왼쪽 클릭하여\n&e웅크리기 + 왼쪽 클릭를 하면 늑대나 오셀롯을 소환할 수 있습니다.
+Guides.Taming.Section.2=&3야수 상식이 작동하는 방법은?\n&e야수 상식을 사용하면 애완동물을 조사하고 늑대와 오셀롯의 통계를 확인할 수 있습니다.\n&ee야수 지식를 사용하려면 늑대나 오셀롯을 왼쪽 클릭합니다.
+Guides.Taming.Section.3=&3천식이 작동하는 방법은?\n&e천식은 늑대의 대상에게 출혈 효과를 발생시키는 기회가 있는 패시브 능력입니다.
+Guides.Taming.Section.4=&3연마된 발톱이 작동하는 방법은?\n&e연마된 발톱은 늑대가 가하는 피해에 추가 피해 보너스를 제공합니다.\n&e데미지 보너스는 길들이기 레벨에 따라 다릅니다.
+Guides.Taming.Section.5=&3환경 인식이 작동하는 방법은?\n&e이 패시브 능력은 늑대가 가시나 용암과 같은 위험에 가까이 갈 때\n&e당신에게 텔레포트할 수 있도록 합니다. 그리고 늑대에게 추락\n&e피해 면역를 부여합니다.
+Guides.Taming.Section.6=&3두꺼운 모피가 작동하는 방법은?\n&e이 패시브 능력은 데미지를 줄이고 늑대를 화염 내성으로 만듭니다.
+Guides.Taming.Section.7=&3충격 방지가 작동하는 방법은?\n&e이 패시브 능력은 폭발로 인한 늑대에게 가해지는 피해를 줄입니다.
+Guides.Taming.Section.8=&3패스트 푸드 서비스가 작동하는 방법은?\n&e이 패시브 능력은 늑대가 공격할 때마다 회복할 기회를 제공합니다.
+##비무장
+Guides.Unarmed.Section.0=&3무장에 관하여:\n&e무장은 주먹으로 무기를 사용할 때 플레이어에게 다양한 전투 보너스를 부여합니다.\n\n&3XP 획득:\n&e주먹으로 몹이나 다른 플레이어에게 가한 데미지 양에 따라 XP를 획득합니다.
+Guides.Unarmed.Section.1=&3광분이 작동하는 방법은?\n&e광분은 우클릭으로 활성화되는 액티브 능력입니다. 광분 모드에서는 50% 더 많은\n&e피해 를 가하고, 흙과 풀과 같은 약한 재료를 즉시 부술 수 있습니다.
+Guides.Unarmed.Section.2=&3철 팔 스타일이 작동하는 방법은?\n&e철 팔 스타일은 주먹으로 몹이나 플레이어를 때릴 때 가하는 피해를 증가시킵니다.
+Guides.Unarmed.Section.3=&3화살 반사가 작동하는 방법은?\n&e화살 반사는 스켈레톤이나 다른 플레이어가 발사한 화살을 반사할 기회를 주는 패시브 능력입니다.\n&e화살은 위험하게 땅에 떨어집니다.
+Guides.Unarmed.Section.4=&3철제 그립이 작동하는 방법은?\n&e철제 그립은 무장해제을 카운터하는 패시브 능력입니다. 무장 레벨이 올라갈수록 무장해제을\n&e방지하는 기회가 늘어납니다.
+Guides.Unarmed.Section.5=&3무장 해제가 작동하는 방법은?\n&e이 패시브 능력은 다른 플레이어를 무장해제하여 대상의 장비가 땅에 떨어지게 만듭니다.
+##벌목
+Guides.Woodcutting.Section.0=&3나무꾼에 관하여:\n&e나무꾼은 나무를 베는 데에 관한 것입니다.\n\n&3XP 획득:\n&e네모난 블록을 부술 때마다 XP가 획득됩니다.
+Guides.Woodcutting.Section.1=&3트리 펠러가 작동하는 방법은?\n&e트리 펠러는 액티브 능력으로, 도끼를 들고 우클릭하여 트리 펠러를 활성화할 수 있습니다.\n&ee전체 트리를 즉시 부수고 모든 나무를 한 번에 드랍합니다.
+Guides.Woodcutting.Section.2=&3잎 블로어가 작동하는 방법은?\n&e잎 블로어는 도끼로 맞을 때 잎 블록이 즉시 부서지도록 하는 패시브 능력입니다.\n&e기본으로 이 능력은 레벨 100에서 잠금 해제됩니다.
+Guides.Woodcutting.Section.3=&3더블 드롭이 작동하는 방법은?\n&e이 패시브 능력은 베어 나무마다 추가 블록을 얻을 기회를 제공합니다.
+#검사
+Inspect.Offline= &c오프라인 플레이어를 확인할 권한이 없습니다!
+Inspect.OfflineStats=오프라인 플레이어 &e{0}의 mcMMO 통계
+Inspect.Stats=&a{0}의 mcMMO 통계
+Inspect.TooFar=해당 플레이어를 확인하기에 너무 멀리 있습니다!
+#아이템
+Item.ChimaeraWing.Fail=&c**카이메라 날개 실패!**
+Item.ChimaeraWing.Pass=**카이메라 날개**
+Item.ChimaeraWing.Name=카이메라 날개
+Item.ChimaeraWing.Lore=&7침대로 이동합니다.
+Item.ChimaeraWing.NotEnough= &e{1}&c이(가) 더 필요합니다! {0}개
+Item.NotEnough= &e{1}&c이(가) 더 필요합니다! {0}개
+Item.Generic.Wait=다시 사용하기 전에 기다려야 합니다! &e({0}초)
+Item.Injured.Wait=최근에 부상당했으므로 사용을 기다려야 합니다. &e({0}초)
+Item.FluxPickaxe.Name=플럭스 곡괭이
+Item.FluxPickaxe.Lore.1=&7광물을 즉시 제련하는 기회가 있습니다.
+Item.FluxPickaxe.Lore.2=&7제련 레벨 {0} 이상 필요
+#텔레포트
+Teleport.Commencing=&7텔레포트 시작 &6({0}초), &7제자리에 서십시오...
Teleport.Cancelled=&4텔레포트 취소됨!
-
-#SKILLS
-Skills.Child=&6(부가 스킬)
-Skills.Disarmed=&4당신은 무장 해제되었습니다!
-Skills.Header=-----[]&a{0}&c[]-----
-Skills.NeedMore=&4당신은 &7{0}&4가 더 필요합니다
-Skills.NeedMore.Extra=&4당신은 &7{0}{1}가 더 필요합니다
-Skills.Parents = 상위 속성들
-Skills.MaxXP=최대 XP
+#스킬
+Skills.Child=&6(하위 스킬)
+Skills.Disarmed=&4무장이 해제되었습니다!
+Skills.Header=-----[] &a{0}&c []-----
+Skills.NeedMore=&4{0}를 더 필요합니다
+Skills.NeedMore.Extra=&4{0}{1}를 더 필요합니다
+Skills.Parents= 상위 스킬
Skills.Stats={0}&a{1}&3 XP(&7{2}&3/&7{3}&3)
Skills.ChildStats={0}&a{1}
-Skills.TooTired=스킬 재 사용 대기시간: ({0}초)
-Skills.TooTired.Named=&7(&6{0}&e {1}s&7)
-Skills.TooTired.Extra=&6{0}&e의 쿨타임: {1}
-Skills.Cancelled={0} 취소됨!
-Skills.ConfirmOrCancel=&a다시 우-클릭을 하면 확인 &6{0}&a. 좌-클릭을 하면 취소가 됩니다.
-Skills.AbilityGateRequirementFail=&7이 슈퍼 능력을 사용하려면 &e{0}&7만큼의 &3{1}&7 스킬 레벨이 필요합니다.
-
-#STATISTICS
+Skills.MaxXP=최대
+Skills.TooTired=다시 사용하려면 너무 피곤합니다. &e({0}초)
+Skills.TooTired.Named=&7(&6{0}&e 초&7)
+Skills.TooTired.Extra=&6{0} &e슈퍼 능력 대기시간 - {1}
+Skills.Cancelled=&6{0} &c취소되었습니다!
+Skills.ConfirmOrCancel=&a다시 오른쪽 클릭하여 &6{0}&a 확인하십시오. 왼쪽 클릭하여 취소하십시오.
+Skills.AbilityGateRequirementFail=&7이 슈퍼 능력을 사용하려면 &3{1}&7 레벨이 더 필요합니다.
+#통계
Stats.Header.Combat=&6-=전투 스킬=-
-Stats.Header.Gathering=&6-=수집 스킬=-
+Stats.Header.Gathering=&6-=채집 스킬=-
Stats.Header.Misc=&6-=기타 스킬=-
-Stats.Own.Stats=&a[mcMMO] 스텟
-
-#PERKS
+Stats.Own.Stats=&a[mcMMO] 통계
+#특전
Perks.XP.Name=경험치
-Perks.XP.Desc=특정 스킬에 경험치 부스트를 받음.
+Perks.XP.Desc=특정 스킬에서 부스트된 XP를 받습니다.
Perks.Lucky.Name=행운
-Perks.Lucky.Desc={0} 스킬과 능력에 33.3%의 더 많은 활성화 확률을 부여합니다.
-Perks.Lucky.Desc.Login=특정 스킬과 능력에 33.3%의 더 많은 활성화 확률을 부여합니다.
-Perks.Lucky.Bonus=&6 ({0} 운좋은 특전과 함께)
+Perks.Lucky.Desc={0} 스킬과 능력이 33.3% 더 활성화됩니다.
+Perks.Lucky.Desc.Login=특정 스킬과 능력이 33.3% 더 활성화됩니다.
+Perks.Lucky.Bonus=&6 (행운 특전으로 {0})
Perks.Cooldowns.Name=빠른 회복
-Perks.Cooldowns.Desc=재사용대기시간을 {0}만큼 줄입니다
-Perks.ActivationTime.Name=인내력
-Perks.ActivationTime.Desc=능력 활성 시간이 {0}초로 증가합니다.
-Perks.ActivationTime.Bonus=&6 ({0}초의 인내력 특전)
-
-#HARDCORE
-Hardcore.Mode.Disabled=&6[mcMMO] 하드코어 모드 {0}가 {1}에 비활성화됨.
-Hardcore.Mode.Enabled=&6[mcMMO] 하드코어 모드 {0}가 {1}에 활성화됨.
-Hardcore.DeathStatLoss.Name=스킬 데스 패널티
-Hardcore.DeathStatLoss.PlayerDeath=&6[mcMMO] &4당신은 죽어서 &9{0}&4 레벨을 잃었습니다.
-Hardcore.DeathStatLoss.PercentageChanged=&6[mcMMO] 스텟 감소 비율이 {0}로 변경되었습니다..
-Hardcore.Vampirism.Name=뱀파이어리즘
-Hardcore.Vampirism.Killer.Failure=&6[mcMMO] &e{0}&7님은 특별한 기술을 가지고 있지 않아 당신이 가져갈 지식이 없습니다.
-Hardcore.Vampirism.Killer.Success=&6[mcMMO] &3당신은 &e{1}&3님으로부터 &9{0}&3 레벨을 훔쳤습니다.
-Hardcore.Vampirism.Victim.Failure=&6[mcMMO] &e{0}&7님은 당신의 지식을 가져갈 수 없었습니다!
-Hardcore.Vampirism.Victim.Success=&6[mcMMO] &e{0}&4님은 당신에게서 &9{1}&4 레벨을 훔쳐갔습니다!
-
+Perks.Cooldowns.Desc=쿨다운 기간을 {0} 만큼 줄입니다.
+Perks.ActivationTime.Name=지속력
+Perks.ActivationTime.Desc=능력 활성화 시간을 {0}초로 증가시킵니다.
+Perks.ActivationTime.Bonus=&6 ({0}초의 지속력 특전)
+#하드코어
+Hardcore.Mode.Disabled=&6[mcMMO] 하드코어 모드 {0}이(가) {1}에 대해 비활성화되었습니다.
+Hardcore.Mode.Enabled=&6[mcMMO] 하드코어 모드 {0}이(가) {1}에 대해 활성화되었습니다.
+Hardcore.DeathStatLoss.Name=스킬 사망 페널티
+Hardcore.DeathStatLoss.PlayerDeath=&6[mcMMO] &4죽음으로 인해 &9{0} 레벨이 손실되었습니다.
+Hardcore.DeathStatLoss.PercentageChanged=&6[mcMMO] 페널티 손실 비율이 {0}로 변경되었습니다.
+Hardcore.Vampirism.Name=흡혈
+Hardcore.Vampirism.Killer.Failure=&6[mcMMO] &e{0}&7님은 지식을 훔치기에는 너무 미숙합니다.
+Hardcore.Vampirism.Killer.Success=&6[mcMMO] &3당신은 &e{1} &9{0} 레벨을 훔쳤습니다.
+Hardcore.Vampirism.Victim.Failure=&6[mcMMO] &e{0}&7님이 당신으로부터 지식을 훔치는 데 실패했습니다!
+Hardcore.Vampirism.Victim.Success=&6[mcMMO] &e{0}&4님이 당신으로부터 &9{1} 레벨을 훔쳤습니다!
+Hardcore.Vampirism.PercentageChanged=&6[mcMMO] 플레이어로부터 지식을 흡수하는 비율이 {0}로 변경되었습니다.
#MOTD
MOTD.Donate=&3기부 정보:
MOTD.Hardcore.Enabled=&6[mcMMO] &3하드코어 모드 활성화됨: &4{0}
-MOTD.Hardcore.DeathStatLoss.Stats=&6[mcMMO] &3데스 패널티 능력: &4{0}%
-MOTD.Hardcore.Vampirism.Stats=&6[mcMMO] &3뱀파이어리즘 스텟 흡수: &4{0}%
-MOTD.PerksPrefix=[mcMMO 특전]
-MOTD.Version=&6[mcMMO] 구동중인 버전 &3{0}
+MOTD.Hardcore.DeathStatLoss.Stats=&6[mcMMO] &3스킬 사망 페널티: &4{0}%
+MOTD.Hardcore.Vampirism.Stats=&6[mcMMO] &3흡혈 스탯 흡수: &4{0}%
+MOTD.PerksPrefix=&6[mcMMO 특전]
+MOTD.Version=&6[mcMMO] 버전 &3{0} 실행 중
MOTD.Website=&6[mcMMO] &a{0}&e - mcMMO 웹사이트
-
-#SMELTING
-Smelting.Ability.FluxMining=유동 채굴 확률: &e{0}
-Smelting.Ability.FuelEfficiency=연료 효율성 배율: &e{0}x
-Smelting.Ability.Locked.0={0}레벨 때 기술이 해제됩니다 (바닐라 XP 부스트)
-Smelting.Ability.Locked.1={0}레벨 때 기술이 해제됩니다 (유동 채굴)
-Smelting.Ability.SecondSmelt=두번째 재련 확률: &e{0}
-Smelting.Ability.VanillaXPBoost=바닐라 XP 배율: &e{0}x
-Smelting.SubSkill.UnderstandingTheArt.Name=예술의 이해
-Smelting.SubSkill.UnderstandingTheArt.Description=아마도 동굴에서 너무 많은 시간을 보내고 있을지도 모릅니다.\n제련의 다양한 속성을 강화합니다.
-Smelting.SubSkill.UnderstandingTheArt.Stat=바닐라 XP 배율: &e{0}배
+#제련
+Smelting.SubSkill.UnderstandingTheArt.Name=Understanding The Art
+Smelting.SubSkill.UnderstandingTheArt.Description=아마도 동굴에서 제련하는 데 너무 많은 시간을 소비하고 있습니다.\n제련의 다양한 속성을 강화합니다.
+Smelting.SubSkill.UnderstandingTheArt.Stat=바닐라 경험치 배율: &e{0}배
+Smelting.Ability.Locked.0={0}+ 스킬까지 잠김 (바닐라 XP 부스트)
+Smelting.Ability.Locked.1={0}+ 스킬까지 잠김 (플럭스 광산)
Smelting.SubSkill.FuelEfficiency.Name=연료 효율성
+Smelting.SubSkill.FuelEfficiency.Description=제련할 때 사용되는 화로 연료의 연소 시간을 증가시킵니다.
Smelting.SubSkill.FuelEfficiency.Stat=연료 효율성 배율: &e{0}배
-Smelting.SubSkill.FuelEfficiency.Description=화로에서 재련시 연료 연소 시간 증가
-Smelting.SubSkill.SecondSmelt.Name=두 번째 제련
-Smelting.SubSkill.SecondSmelt.Description=제련시 얻는 자원 2배
-Smelting.SubSkill.SecondSmelt.Stat=두 번째 재련 확률
+Smelting.SubSkill.SecondSmelt.Name=이중 제련
+Smelting.SubSkill.SecondSmelt.Description=제련으로 얻은 자원을 두 배로 늘립니다.
+Smelting.SubSkill.SecondSmelt.Stat=이중 제련 확률
Smelting.Effect.4=바닐라 XP 부스트
-Smelting.Effect.5=제련중 바닐라 XP 얻기 증가
-Smelting.SubSkill.FluxMining.Name=유동 채굴
-Smelting.SubSkill.FluxMining.Description=채굴중 광물 즉시 재련 확률
-Smelting.SubSkill.FluxMining.Stat=유동 채굴 확률
-Smelting.FluxMining.Success=&a광물이 재련되었습니다!
-Smelting.Listener=제련(Smelting):
+Smelting.Effect.5=제련 시 얻는 바닐라 XP를 증가시킵니다.
+Smelting.SubSkill.FluxMining.Name=플럭스 광산
+Smelting.SubSkill.FluxMining.Description=광물이 채굴되는 동안 즉시 제련되는 확률이 있습니다.
+Smelting.SubSkill.FluxMining.Stat=플럭스 광산 확률
+Smelting.Listener=제련:
Smelting.SkillName=제련
-
-
-#COMMAND DESCRIPTIONS
-Commands.Description.addlevels=mcMMO 레벨을 유저에게 추가
-Commands.Description.adminchat=mcMMO 관리자 채팅 켜기/끄기나 관리자 채팅 메세지 보내기
-Commands.Description.addxp=mcMMO 경험치를 유저에게 추가
-Commands.Description.hardcore=mcMMO 하드코어 확률 수정이나 하드코어 모드 켜기/끄기
-Commands.Description.inspect=다른 플레이어의 mcMMO 자세한 정보 보기
-Commands.Description.mcability=mcMMO 우-클릭 능력 켜기/끄기
-Commands.Description.mccooldown=모든 mcMMO 능력 재 사용 대기시간 보기
-Commands.Description.mcchatspy=mcMMO 파티 채팅 감시 켜기/끄기
-Commands.Description.mcgod=mcMMO 불사신-모드 켜기/끄기
-Commands.Description.mchud=mcMMO HUD 방식 변경
-Commands.Description.mcmmo=mcMMO 제작자 설명 보기
-Commands.Description.mcnotify=mcMMO 능력 채팅 알림 보기 켜기/끄기
-Commands.Description.mcpurge={0} 달 이상 접속안한 유저의 mcMMO 레벨과 유저를 mcMMO 데이터베이스에서 초기화시킴
-Commands.Description.mcrank=플레이어 mcMMO 순위 보기
-Commands.Description.mcrefresh=모든 mcMMO 쿨다운 초기화
-Commands.Description.mcremove=유저 mcMMO 데이터베이스 삭제
-Commands.Description.mcscoreboard=당신의 mcMMO 점수판 관리
-Commands.Description.mcstats=자신의 mcMMO 레벨과 XP 보기
-Commands.Description.mctop=mcMMO 점수표 보기
-Commands.Description.mmoedit=유저의 mcMMO 레벨 수정
-Commands.Description.mmodebug=블록을 때릴 때 유용한 정보를 출력하는 디버그 모드를 켜기/끄기.
-Commands.Description.mmoupdate=mcMMO 데이터베이스를 flatfile에서 MySQL로 전환
-Commands.Description.mcconvert=데이터베이스 타입 또는 경험 공식 타입 전환
-Commands.Description.mmoshowdb=현재 데이터베이스 타입 이름 보기(나중에 /mmoupdate와 함께 쓰입니다)
-Commands.Description.party=다양한 mcMMO 파티 설정 관리
-Commands.Description.partychat=mcMMO 파티 채팅 켜기/끄기나 파티 채팅 메세지 보내기
-Commands.Description.ptp=mcMMO 파티 맴버 텔레포트
-Commands.Description.Skill=mcMMO 기술 {0}의 자세한 정보 보기
-Commands.Description.skillreset=유저의 mcMMO 레벨 재설정
-Commands.Description.vampirism=mcMMO 뱀파이어리즘 비율이나 뱀파이어리즘 모드 켜기/끄기
-Commands.Description.xplock=명확한 mcMMO 기술의 mcMMO xp 바를 잠금
-Commands.Description.xprate=mcMMO XP 배율 수정이나 mcMMO XP 이벤트 시작
-
-#UPDATE CHECKER
-UpdateChecker.outdated=당신은 mcMMO 구버전을 사용중입니다!
-UpdateChecker.newavailable=신 버전이 Spigot에 업로드되어 있습니다.
-
-#SCOREBOARD HEADERS
-Scoreboard.Header.PlayerStats=mcMMO 스텟
-Scoreboard.Header.PlayerCooldowns=mcMMO 재 사용 대기시간
-Scoreboard.Header.PlayerRank=mcMMO 순위
-Scoreboard.Header.PlayerInspect=mcMMO 스텟:
-Scoreboard.Header.PowerLevel=총 레벨
-Scoreboard.Misc.PowerLevel=&6총 레벨
+#명령어 설명
+Commands.Description.addlevels=사용자에게 mcMMO 레벨을 추가합니다.
+Commands.Description.adminchat=mcMMO 관리자 채팅을 켜거나 끕니다. 또는 관리자 채팅 메시지를 보냅니다.
+Commands.Description.addxp=사용자에게 mcMMO XP를 추가합니다.
+Commands.Description.hardcore=mcMMO 하드코어 백분율을 수정하거나 하드코어 모드를 켜거나 끕니다.
+Commands.Description.inspect=다른 플레이어에 대한 자세한 mcMMO 정보를 확인합니다.
+Commands.Description.mcability=오른쪽 클릭으로 mcMMO 능력 사용을 준비하거나 해제합니다.
+Commands.Description.mccooldown=모든 mcMMO 능력 쿨다운을 보여줍니다.
+Commands.Description.mcchatspy=mcMMO 파티 채팅 감시를 켜거나 끕니다.
+Commands.Description.mcgod=mcMMO 갓 모드를 켜거나 끕니다.
+Commands.Description.mchud=mcMMO HUD 스타일을 변경합니다.
+Commands.Description.mcmmo=mcMMO에 대한 간략한 설명을 표시합니다.
+Commands.Description.mcnotify=mcMMO 능력 채팅 표시 알림을 켜거나 끕니다.
+Commands.Description.mcpurge=mcMMO 데이터베이스에서 mcMMO 레벨이 없거나 지정된 개월 동안 접속하지 않은 사용자를 제거합니다.
+Commands.Description.mcrank=플레이어의 mcMMO 순위를 표시합니다.
+Commands.Description.mcrefresh=mcMMO의 모든 쿨다운을 새로 고칩니다.
+Commands.Description.mcremove=사용자를 mcMMO 데이터베이스에서 제거합니다.
+Commands.Description.mcscoreboard=mcMMO 스코어보드를 관리합니다.
+Commands.Description.mcstats=사용자의 mcMMO 레벨과 XP를 표시합니다.
+Commands.Description.mctop=mcMMO 리더보드를 표시합니다.
+Commands.Description.mmoedit=사용자의 mcMMO 레벨을 편집합니다.
+Commands.Description.mmodebug=디버그 모드를 토글하여 블록을 탭할 때 유용한 정보를 출력합니다.
+Commands.Description.mmoupdate=이전 데이터베이스에서 현재 데이터베이스로 mcMMO 데이터베이스를 마이그레이션합니다.
+Commands.Description.mcconvert=데이터베이스 유형이나 경험치 공식 유형을 변환합니다.
+Commands.Description.mmoshowdb=현재 데이터베이스 유형의 이름을 표시합니다 (/mmoupdate에서 사용됨).
+Commands.Description.party=다양한 mcMMO 파티 설정을 제어합니다.
+Commands.Description.partychat=mcMMO 파티 채팅을 켜거나 끕니다. 또는 파티 채팅 메시지를 보냅니다.
+Commands.Description.ptp=mcMMO 파티 멤버로 텔레포트합니다.
+Commands.Description.Skill={0}에 대한 자세한 mcMMO 스킬 정보를 표시합니다.
+Commands.Description.skillreset=사용자의 mcMMO 레벨을 재설정합니다.
+Commands.Description.vampirism=mcMMO 흡혈 백분율을 수정하거나 흡혈 모드를 켜거나 끕니다.
+Commands.Description.xplock=mcMMO XP 바를 특정 mcMMO 스킬에 잠급니다.
+Commands.Description.xprate=mcMMO XP 비율을 수정하거나 mcMMO XP 이벤트를 시작합니다.
+#업데이트 체크
+UpdateChecker.Outdated=사용 중인 mcMMO 버전이 오래되었습니다!
+UpdateChecker.NewAvailable=Spigot에서 새 버전을 사용할 수 있습니다.
+#스코어보드 헤더
+Scoreboard.Header.PlayerStats=&emcMMO 통계
+Scoreboard.Header.PlayerCooldowns=&emcMMO 쿨다운
+Scoreboard.Header.PlayerRank=&emcMMO 순위
+Scoreboard.Header.PlayerInspect=&emcMMO 통계: {0}
+Scoreboard.Header.PowerLevel=&c파워 레벨
+Scoreboard.Misc.PowerLevel=&6파워 레벨
Scoreboard.Misc.Level=&3레벨
Scoreboard.Misc.CurrentXP=&a현재 XP
-Scoreboard.Misc.RemainingXP=남은 XP
-Scoreboard.Misc.Cooldown=&d재 사용 대기시간
-Scoreboard.Misc.Overall=&6종합
+Scoreboard.Misc.RemainingXP=&e남은 XP
+Scoreboard.Misc.Cooldown=&d쿨다운
+Scoreboard.Misc.Overall=&6전체
Scoreboard.Misc.Ability=능력
-
-#DATABASE RECOVERY
-Profile.PendingLoad=&c당신의 mcMMO 프로파일을 아직 불러오지 못했습니다.
-Profile.Loading.Success=&a당신의 mcMMO 프로파일을 불러왔습니다.
-Profile.Loading.Failure=mcMMO는 여전히 당신의 데이터를 읽을 수 없습니다. 당신은 아마도 &b서버관리자와 연락&c하기를 원할 것입니다.\n&e당신은 여전히 서버에서 게임중이지만, 당신은 &lmcMMO 레벨이 없고&e 당신이 얻은 어느 XP도 &l저장되지 않을 겁니다&e.
-Profile.Loading.AdminFailureNotice=&4[A]&c mcMMO는 &e{0}&c 플레이어 데이터 읽기가 불가능합니다. &d당신의 데이터베이스 설치를 검사해주세요.
-Profile.Loading.FailurePlayer=mcMMO가 데이터를 로드하는 데 문제가 발생했습니다. 로드 시도 횟수: {0}회. 이 문제에 대해 서버 관리자에게 문의하십시오. 데이터가 로드되지 않은 동안 XP를 획득하거나 기술을 사용할 수 없습니다.
-Profile.Loading.FailureNotice=&4[A]&c mcMMO는 &e{0}&c 플레이어 데이터를 로드하지 못했습니다. &d데이터베이스 설정을 확인하십시오. 현재까지 시도한 횟수: {1}.
-Commands.XPBar.Usage=올바른 사용법: /mmoxpbar <스킬명 | reset>
-Commands.Description.mmoxpbar=mcMMO XP 바에 대한 플레이어 설정
-Commands.Description.mmocompat=mcMMO의 호환 모드 또는 완전한 기능 여부에 대한 정보
-
+#데이터베이스 복구
+Profile.PendingLoad=&c당신의 mcMMO 플레이어 데이터가 아직로드되지 않았습니다.
+Profile.Loading.Success=&a당신의 mcMMO 프로필이 로드되었습니다.
+Profile.Loading.FailurePlayer=&cmcMMO는 데이터로드에 문제가 있습니다. 우리는 &a{0}번&c 시도했습니다. &c이 문제에 대해 서버 관리자에게 문의하십시오. mcMMO는 데이터가로드되지 않은 동안 XP를 획득하거나 기술을 사용할 수 없습니다.
+Profile.Loading.FailureNotice=&4[A]&c mcMMO가 &e{0}&c의 플레이어 데이터를로드하지 못했습니다. &d데이터베이스 설정을 확인하십시오. 지금까지 시도한 시도 {1}.
#Holiday
-Holiday.AprilFools.Levelup=&6{0}은(는) 이제 &a{1}&6레벨입니다!
-Holiday.Anniversary=&9{0}주년을 축하합니다!\n&9nossr50의 모든 작업과 개발자들을 기리기 위해 불꽃쇼를 선물합니다!
-
+Holiday.AprilFools.Levelup=&6{0}님이 이제 레벨 &a{1}&6입니다!
+Holiday.Anniversary=&9{0}주년을 축하합니다!\n&9모든 nossr50의 작업과 개발자들을 기념하여 불꽃놀이를 여기에 선사합니다!
#Reminder Messages
-Reminder.Squelched=&7알림: 현재 mcMMO로부터 알림을 받지 않고 있습니다. 알림을 활성화하려면 다시 /mcnotify 명령을 실행하십시오. 이것은 자동으로 매 시간마다 알림이 전송되는 것입니다.
-
+Reminder.Squelched=&7리마인더: 당신은 현재 mcMMO로부터 알림을받지 않습니다. 알림을 받으려면 다시 /mcnotify 명령을 실행하십시오. 이것은 자동으로 시간당 알림입니다.
#Locale
-Locale.Reloaded=&a번역을 리로드했습니다!
-
+Locale.Reloaded=&a로케일이 다시로드되었습니다!
#Player Leveling Stuff
-LevelCap.PowerLevel=&6(&amcMMO&6) &e파워 레벨 &c{0}&e에 도달했습니다. 이제부터 스킬 레벨이 상승하지 않습니다.
-LevelCap.Skill=&6(&amcMMO&6) &e&6{1}&e 스킬의 레벨 캡 &c{0}&e에 도달했습니다. 이제부터 이 스킬의 레벨이 상승하지 않습니다.
-Commands.XPBar.Usage=올바른 사용법: /mmoxpbar <스킬명 | reset>
+LevelCap.PowerLevel=&6(&amcMMO&6) &e파워 레벨 상한에 도달했습니다: &c{0}&e. 여기서부터는 더 이상 기술을 레벨링하지 않습니다.
+LevelCap.Skill=&6(&amcMMO&6) &e&6{1}&e의 레벨 상한에 도달했습니다: &c{0}&e. 여기서부터는 이 기술의 레벨이 증가하지 않습니다.
+Commands.XPBar.Usage=올바른 사용법은 /mmoxpbar <기술명 | 재설정> <표시 | 숨기기> 입니다.
Commands.Description.mmoxpbar=mcMMO XP 바에 대한 플레이어 설정
-Commands.Description.mmocompat=mcMMO의 호환 모드 또는 완전한 기능 여부에 대한 정보
-Compatibility.Layer.Unsupported=&6{0}&6의 호환성은 이 버전의 Minecraft에서 지원되지 않습니다.
-Compatibility.Layer.PartialSupport=&6{0}&6의 호환성은 이 버전의 Minecraft에서 완전히 지원되지 않지만, mcMMO는 일부 기능을 에뮬레이션하기 위해 보조 시스템을 실행 중입니다.
-Commands.XPBar.DisableAll=&6모든 mcMMO XP 바가 비활성화되었습니다. 기본 설정으로 복원하려면 /mmoxpbar reset을 사용하세요.
-
+Commands.Description.mmocompat=mcMMO에 대한 정보 및 호환 모드 또는 완전 기능적 모드 여부.
+Compatibility.Layer.Unsupported=&6{0}&6의 호환성은 이 Minecraft 버전에서 지원되지 않습니다.
+Compatibility.Layer.PartialSupport=&6{0}&6의 호환성은 이 Minecraft 버전에서 완전히 지원되지 않지만, mcMMO는 일부 부족한 기능을 모방하기위한 보조 시스템을 실행 중입니다.
+Commands.XPBar.DisableAll=&6모든 mcMMO XP 바가 비활성화되었습니다. 기본 설정으로 복원하려면 /mmoxpbar 재설정을 사용하세요.
#Modern Chat Settings
Chat.Style.Admin=&b(A) &r{0} &b\u2192 &r{1}
Chat.Style.Party=&a(P) &r{0} &a\u2192 &r{1}
Chat.Style.Party.Leader=&a(P) &r{0} &6\u2192 &r{1}
Chat.Identity.Console=&6* 콘솔 *
-Chat.Channel.On=&6(&amcMMO-Chat&6) &e당신의 채팅 메시지는 이제 &a{0}&e 채팅 채널로 자동 전달됩니다.
-Chat.Channel.Off=&6(&amcMMO-Chat&6) &7당신의 채팅 메시지는 더 이상 특정 채팅 채널로 자동 전달되지 않습니다.
+Chat.Channel.On=&6(&amcMMO-채팅&6) &e당신의 채팅 메시지는 이제 &a{0}&e 채널로 자동 전달됩니다.
+Chat.Channel.Off=&6(&amcMMO-채팅&6) &7당신의 채팅 메시지는 더 이상 특정 채팅 채널로 자동 전달되지 않습니다.
Chat.Spy.Party=&6[&eSPY&6-&a{2}&6] &r{0} &b\u2192 &r{1}
-Broadcasts.LevelUpMilestone=&6(&amcMMO&6) {0}&7님이 &3{2}&7에서 레벨 &a{1}&7에 도달했습니다!
-Broadcasts.PowerLevelUpMilestone=&6(&amcMMO&6) {0}&7님이 파워 레벨 &a{1}&7에 도달했습니다!
-Scoreboard.Recovery=mcMMO 점수판을 복구하는 중입니다...
-Scoreboard.Disabled=이 서버의 mcMMO 점수판이 비활성화되었습니다. 이 설정은 mcMMO/config.yml에서 찾을 수 있습니다.
-Scoreboard.NotSetupYet=당신의 mcMMO 점수판이 아직 설정되지 않았습니다. 나중에 다시 시도해보세요.
+Broadcasts.LevelUpMilestone=&6(&amcMMO&6) {0}&7님이 &3{2}&7에서 레벨 &a{1}&7달렸습니다!
+Broadcasts.PowerLevelUpMilestone=&6(&amcMMO&6) {0}&7님이 &a{1}&7단계의 파워 레벨에 도달했습니다!
+Scoreboard.Recovery=mcMMO 스코어보드를 복구하는 중입니다...
+Scoreboard.Disabled=이 서버의 mcMMO 스코어보드가 비활성화되었습니다. 이 설정은 mcMMO/config.yml에서 찾을 수 있습니다.
+Scoreboard.NotSetupYet=당신의 mcMMO 스코어보드가 아직 설정되지 않았습니다. 나중에 다시 시도하십시오.
diff --git a/src/main/resources/plugin.yml b/src/main/resources/plugin.yml
index a8b56712c..198d465fc 100644
--- a/src/main/resources/plugin.yml
+++ b/src/main/resources/plugin.yml
@@ -20,9 +20,6 @@ folia-supported: true
api-version: 1.13
commands:
-# mmodroptreasures:
-# description: An admin command used to spawn treasure drops
-# permission: mcmmo.commands.droptreasures
mmoxpbar:
aliases: xpbarsettings
description: Change XP bar settings
@@ -124,12 +121,18 @@ commands:
archery:
description: Detailed mcMMO skill info
permission: mcmmo.commands.archery
+ crossbows:
+ description: Detailed mcMMO skill info
+ permission: mcmmo.commands.crossbows
swords:
description: Detailed mcMMO skill info
permission: mcmmo.commands.swords
taming:
description: Detailed mcMMO skill info
permission: mcmmo.commands.taming
+ tridents:
+ description: Detailed mcMMO skill info
+ permission: mcmmo.commands.tridents
unarmed:
description: Detailed mcMMO skill info
permission: mcmmo.commands.unarmed
@@ -151,6 +154,10 @@ commands:
salvage:
description: Detailed mcMMO skill info
permission: mcmmo.commands.salvage
+ mmopower:
+ description: Shows skill mastery and power level info
+ permission: mcmmo.commands.mmopower
+ aliases: [mmopowerlevel, powerlevel]
adminchat:
aliases: [ac, a]
description: Toggle Admin chat or send admin chat messages
@@ -234,6 +241,7 @@ permissions:
mcmmo.ability.alchemy.all: true
mcmmo.ability.archery.all: true
mcmmo.ability.axes.all: true
+ mcmmo.ability.crossbows.all: true
mcmmo.ability.excavation.all: true
mcmmo.ability.fishing.all: true
mcmmo.ability.herbalism.all: true
@@ -243,6 +251,7 @@ permissions:
mcmmo.ability.smelting.all: true
mcmmo.ability.swords.all: true
mcmmo.ability.taming.all: true
+ mcmmo.ability.tridents.all: true
mcmmo.ability.unarmed.all: true
mcmmo.ability.woodcutting.all: true
mcmmo.ability.acrobatics.*:
@@ -281,12 +290,15 @@ permissions:
mcmmo.ability.archery.all:
description: Allows access to all Archery abilities
children:
+ mcmmo.ability.archery.explosiveshot: true
mcmmo.ability.archery.skillshot: true
mcmmo.ability.archery.daze: true
mcmmo.ability.archery.arrowretrieval: true
mcmmo.ability.archery.archerylimitbreak: true
+ mcmmo.ability.archery.explosiveshot:
+ description: Allows access to the Explosive Shot super ability for Archery
mcmmo.ability.archery.archerylimitbreak:
- description: Adds damage to bows and crossbows
+ description: Adds damage to bows
mcmmo.ability.archery.skillshot:
description: Allows bonus damage from the Archery SkillShot ability
mcmmo.ability.archery.daze:
@@ -319,6 +331,22 @@ permissions:
description: Allows access to the Impact ability
mcmmo.ability.axes.skullsplitter:
description: Allows access to the Skull Splitter ability
+ mcmmo.ability.crossbows.*:
+ description: Allows access to all Crossbows abilities
+ children:
+ mcmmo.ability.crossbows.all: true
+ mcmmo.ability.crossbows.all:
+ description: Allows access to all Crossbows abilities
+ children:
+ mcmmo.ability.crossbows.trickshot: true
+ mcmmo.ability.crossbows.poweredshot: true
+ mcmmo.ability.crossbows.crossbowslimitbreak: true
+ mcmmo.ability.crossbows.crossbowslimitbreak:
+ description: Adds damage to crossbows
+ mcmmo.ability.crossbows.trickshot:
+ description: Allows access to the Trick Shot ability
+ mcmmo.ability.crossbows.poweredshot:
+ description: Allows access to the Powered Shot ability
mcmmo.ability.excavation.*:
default: false
description: Allows access to all Excavation abilities
@@ -376,6 +404,9 @@ permissions:
mcmmo.ability.herbalism.greenthumb.all: true
mcmmo.ability.herbalism.hylianluck: true
mcmmo.ability.herbalism.shroomthumb: true
+ mcmmo.ability.herbalism.verdantbounty: true
+ mcmmo.ability.herbalism.verdantbounty:
+ description: Allows access to end game progression for Herbalism
mcmmo.ability.herbalism.doubledrops:
description: Allows double drop chance from Herbalism
mcmmo.ability.herbalism.farmersdiet:
@@ -456,6 +487,7 @@ permissions:
mcmmo.ability.mining.blastmining.all: true
mcmmo.ability.mining.doubledrops: true
mcmmo.ability.mining.superbreaker: true
+ mcmmo.ability.mining.motherlode: true
mcmmo.ability.mining.blastmining.*:
default: false
description: Allows access to all Blast Mining abilities
@@ -473,6 +505,8 @@ permissions:
description: Allows access to the Demolitions Expertise ability
mcmmo.ability.mining.blastmining.detonate:
description: Allows for remote TNT detonation
+ mcmmo.ability.mining.motherlode:
+ description: Allows access to mother lode subskill
mcmmo.ability.mining.doubledrops:
description: Allows double drop chance when mining
mcmmo.ability.mining.superbreaker:
@@ -677,6 +711,20 @@ permissions:
description: Allows access to the Thick Fur ability
mcmmo.ability.taming.pummel:
description: Allows access to the Pummel ability
+ mcmmo.ability.tridents.*:
+ default: false
+ description: Allows access to all Trident abilities
+ children:
+ mcmmo.ability.tridents.all: true
+ mcmmo.ability.tridents.all:
+ description: Allows access to all Trident abilities
+ children:
+ mcmmo.ability.tridents.impale: true
+ mcmmo.ability.tridents.tridentslimitbreak: true
+ mcmmo.ability.tridents.impale:
+ description: Allows access to tridents Impale ability
+ mcmmo.ability.tridents.tridentslimitbreak:
+ description: Adds damage to tridents
mcmmo.ability.unarmed.*:
default: false
description: Allows access to all Unarmed abilities
@@ -721,6 +769,9 @@ permissions:
mcmmo.ability.woodcutting.knockonwood: true
mcmmo.ability.woodcutting.leafblower: true
mcmmo.ability.woodcutting.treefeller: true
+ mcmmo.ability.woodcutting.cleancuts: true
+ mcmmo.ability.woodcutting.cleancuts:
+ description: Allows access to end game progression for Woodcutting
mcmmo.ability.woodcutting.knockonwood:
description: Allows access to Knock on Wood subskill
mcmmo.ability.woodcutting.splinter:
@@ -802,6 +853,8 @@ permissions:
mcmmo.commands.alchemy: true
mcmmo.commands.archery: true
mcmmo.commands.axes: true
+ mcmmo.commands.crossbows: true
+ mcmmo.commands.tridents: true
mcmmo.commands.excavation: true
mcmmo.commands.fishing: true
mcmmo.commands.herbalism: true
@@ -824,6 +877,7 @@ permissions:
mcmmo.commands.taming: true
mcmmo.commands.unarmed: true
mcmmo.commands.woodcutting: true
+ mcmmo.commands.mmopower: true
mcmmo.commands.defaultsop:
description: Implies all default op mcmmo.commands permissions.
children:
@@ -871,29 +925,16 @@ permissions:
description: Allows access to the archery command
mcmmo.commands.axes:
description: Allows access to the axes command
+ mcmmo.commands.crossbows:
+ description: Allows access to the crossbows command
mcmmo.commands.excavation:
description: Allows access to the excavation command
mcmmo.commands.fishing:
description: Allows access to the fishing command
-# mcmmo.commands.hardcore.*:
-# default: false
-# description: Implies access to all mcmmo.commands.hardcore permissions
-# children:
-# mcmmo.commands.hardcore.all: true
-# mcmmo.commands.hardcore.all:
-# description: Implies access to all mcmmo.commands.hardcore permissions
-# children:
-# mcmmo.commands.hardcore: true
-# mcmmo.commands.hardcore.modify: true
-# mcmmo.commands.hardcore.toggle: true
-# mcmmo.commands.hardcore:
-# description: Allows access to the hardcore command
-# mcmmo.commands.hardcore.modify:
-# description: Allows access to the hardcore command to modify the hardcore rate
-# mcmmo.commands.hardcore.toggle:
-# description: Allows access to the hardcore command to toggle hardcore on/off
mcmmo.commands.herbalism:
description: Allows access to the herbalism command
+ mcmmo.commands.tridents:
+ description: Allows access to the tridents command
mcmmo.commands.inspect.*:
default: false
description: Implies access to all mcmmo.commands.inspect permissions
@@ -999,6 +1040,7 @@ permissions:
mcmmo.commands.mctop.alchemy: true
mcmmo.commands.mctop.archery: true
mcmmo.commands.mctop.axes: true
+ mcmmo.commands.mctop.crossbows: true
mcmmo.commands.mctop.excavation: true
mcmmo.commands.mctop.fishing: true
mcmmo.commands.mctop.herbalism: true
@@ -1008,6 +1050,7 @@ permissions:
mcmmo.commands.mctop.smelting: true
mcmmo.commands.mctop.swords: true
mcmmo.commands.mctop.taming: true
+ mcmmo.commands.mctop.tridents: true
mcmmo.commands.mctop.unarmed: true
mcmmo.commands.mctop.woodcutting: true
mcmmo.commands.mctop:
@@ -1020,6 +1063,8 @@ permissions:
description: Allows access to the mctop command for archery
mcmmo.commands.mctop.axes:
description: Allows access to the mctop command for axes
+ mcmmo.commands.mctop.crossbows:
+ description: Allows access to the mctop command for crossbows
mcmmo.commands.mctop.excavation:
description: Allows access to the mctop command for excavation
mcmmo.commands.mctop.fishing:
@@ -1038,6 +1083,8 @@ permissions:
description: Allows access to the mctop command for swords
mcmmo.commands.mctop.taming:
description: Allows access to the mctop command for taming
+ mcmmo.commands.mctop.tridents:
+ description: Allows access to the mctop command for tridents
mcmmo.commands.mctop.unarmed:
description: Allows access to the mctop command for unarmed
mcmmo.commands.mctop.woodcutting:
@@ -1452,6 +1499,9 @@ permissions:
mcmmo.perks.lucky.axes:
default: false
description: Gives Axes abilities & skills a 33.3% better chance to activate.
+ mcmmo.perks.lucky.crossbows:
+ default: false
+ description: Gives Crossbows abilities & skills a 33.3% better chance to activate.
mcmmo.perks.lucky.excavation:
default: false
description: Gives Excavation abilities & skills a 33.3% better chance to activate.
@@ -1479,6 +1529,9 @@ permissions:
mcmmo.perks.lucky.taming:
default: false
description: Gives Taming abilities & skills a 33.3% better chance to activate.
+ mcmmo.perks.lucky.tridents:
+ default: false
+ description: Gives Tridents abilities & skills a 33.3% better chance to activate.
mcmmo.perks.lucky.unarmed:
default: false
description: Gives Unarmed abilities & skills a 33.3% better chance to activate.
@@ -1520,6 +1573,7 @@ permissions:
mcmmo.perks.xp.150percentboost.alchemy: true
mcmmo.perks.xp.150percentboost.archery: true
mcmmo.perks.xp.150percentboost.axes: true
+ mcmmo.perks.xp.150percentboost.crossbows: true
mcmmo.perks.xp.150percentboost.excavation: true
mcmmo.perks.xp.150percentboost.fishing: true
mcmmo.perks.xp.150percentboost.herbalism: true
@@ -1528,6 +1582,7 @@ permissions:
mcmmo.perks.xp.150percentboost.smelting: true
mcmmo.perks.xp.150percentboost.swords: true
mcmmo.perks.xp.150percentboost.taming: true
+ mcmmo.perks.xp.150percentboost.tridents: true
mcmmo.perks.xp.150percentboost.unarmed: true
mcmmo.perks.xp.150percentboost.woodcutting: true
mcmmo.perks.xp.150percentboost.acrobatics:
@@ -1542,6 +1597,9 @@ permissions:
mcmmo.perks.xp.150percentboost.axes:
default: false
description: Multiplies incoming Axes XP by 2.5
+ mcmmo.perks.xp.150percentboost.crossbows:
+ default: false
+ description: Multiplies incoming Crossbows XP by 2.5
mcmmo.perks.xp.150percentboost.excavation:
default: false
description: Multiplies incoming Excavation XP by 2.5
@@ -1566,6 +1624,9 @@ permissions:
mcmmo.perks.xp.150percentboost.taming:
default: false
description: Multiplies incoming Taming XP by 2.5
+ mcmmo.perks.xp.150percentboost.tridents:
+ default: false
+ description: Multiplies incoming Tridents XP by 2.5
mcmmo.perks.xp.150percentboost.unarmed:
default: false
description: Multiplies incoming Unarmed XP by 2.5
@@ -1590,6 +1651,7 @@ permissions:
mcmmo.perks.xp.50percentboost.alchemy: true
mcmmo.perks.xp.50percentboost.archery: true
mcmmo.perks.xp.50percentboost.axes: true
+ mcmmo.perks.xp.50percentboost.crossbows: true
mcmmo.perks.xp.50percentboost.excavation: true
mcmmo.perks.xp.50percentboost.fishing: true
mcmmo.perks.xp.50percentboost.herbalism: true
@@ -1598,6 +1660,7 @@ permissions:
mcmmo.perks.xp.50percentboost.smelting: true
mcmmo.perks.xp.50percentboost.swords: true
mcmmo.perks.xp.50percentboost.taming: true
+ mcmmo.perks.xp.50percentboost.tridents: true
mcmmo.perks.xp.50percentboost.unarmed: true
mcmmo.perks.xp.50percentboost.woodcutting: true
mcmmo.perks.xp.50percentboost.acrobatics:
@@ -1612,6 +1675,9 @@ permissions:
mcmmo.perks.xp.50percentboost.axes:
default: false
description: Multiplies incoming Axes XP by 1.5
+ mcmmo.perks.xp.50percentboost.crossbows:
+ default: false
+ description: Multiplies incoming Crossbows XP by 1.5
mcmmo.perks.xp.50percentboost.excavation:
default: false
description: Multiplies incoming Excavation XP by 1.5
@@ -1636,6 +1702,9 @@ permissions:
mcmmo.perks.xp.50percentboost.taming:
default: false
description: Multiplies incoming Taming XP by 1.5
+ mcmmo.perks.xp.50percentboost.tridents:
+ default: false
+ description: Multiplies incoming Tridents XP by 1.5
mcmmo.perks.xp.50percentboost.unarmed:
default: false
description: Multiplies incoming Unarmed XP by 1.5
@@ -1656,20 +1725,22 @@ permissions:
default: false
description: Multiplies incoming XP by 1.25
children:
- mcmmo.perks.xp.25percentboost.acrobatics: true
- mcmmo.perks.xp.25percentboost.alchemy: true
- mcmmo.perks.xp.25percentboost.archery: true
- mcmmo.perks.xp.25percentboost.axes: true
- mcmmo.perks.xp.25percentboost.excavation: true
- mcmmo.perks.xp.25percentboost.fishing: true
- mcmmo.perks.xp.25percentboost.herbalism: true
- mcmmo.perks.xp.25percentboost.mining: true
- mcmmo.perks.xp.25percentboost.repair: true
- mcmmo.perks.xp.25percentboost.smelting: true
- mcmmo.perks.xp.25percentboost.swords: true
- mcmmo.perks.xp.25percentboost.taming: true
- mcmmo.perks.xp.25percentboost.unarmed: true
- mcmmo.perks.xp.25percentboost.woodcutting: true
+ mcmmo.perks.xp.25percentboost.acrobatics: true
+ mcmmo.perks.xp.25percentboost.alchemy: true
+ mcmmo.perks.xp.25percentboost.archery: true
+ mcmmo.perks.xp.25percentboost.axes: true
+ mcmmo.perks.xp.25percentboost.crossbows: true
+ mcmmo.perks.xp.25percentboost.excavation: true
+ mcmmo.perks.xp.25percentboost.fishing: true
+ mcmmo.perks.xp.25percentboost.herbalism: true
+ mcmmo.perks.xp.25percentboost.mining: true
+ mcmmo.perks.xp.25percentboost.repair: true
+ mcmmo.perks.xp.25percentboost.smelting: true
+ mcmmo.perks.xp.25percentboost.swords: true
+ mcmmo.perks.xp.25percentboost.taming: true
+ mcmmo.perks.xp.25percentboost.tridents: true
+ mcmmo.perks.xp.25percentboost.unarmed: true
+ mcmmo.perks.xp.25percentboost.woodcutting: true
mcmmo.perks.xp.25percentboost.acrobatics:
default: false
description: Multiplies incoming Acrobatics XP by 1.25
@@ -1682,6 +1753,9 @@ permissions:
mcmmo.perks.xp.25percentboost.axes:
default: false
description: Multiplies incoming Axes XP by 1.25
+ mcmmo.perks.xp.25percentboost.crossbows:
+ default: false
+ description: Multiplies incoming Crossbows XP by 1.25
mcmmo.perks.xp.25percentboost.excavation:
default: false
description: Multiplies incoming Excavation XP by 1.25
@@ -1706,6 +1780,9 @@ permissions:
mcmmo.perks.xp.25percentboost.taming:
default: false
description: Multiplies incoming Taming XP by 1.25
+ mcmmo.perks.xp.25percentboost.tridents:
+ default: false
+ description: Multiplies incoming Tridents XP by 1.25
mcmmo.perks.xp.25percentboost.unarmed:
default: false
description: Multiplies incoming Unarmed XP by 1.5
@@ -1730,6 +1807,7 @@ permissions:
mcmmo.perks.xp.10percentboost.alchemy: true
mcmmo.perks.xp.10percentboost.archery: true
mcmmo.perks.xp.10percentboost.axes: true
+ mcmmo.perks.xp.10percentboost.crossbows: true
mcmmo.perks.xp.10percentboost.excavation: true
mcmmo.perks.xp.10percentboost.fishing: true
mcmmo.perks.xp.10percentboost.herbalism: true
@@ -1738,6 +1816,7 @@ permissions:
mcmmo.perks.xp.10percentboost.smelting: true
mcmmo.perks.xp.10percentboost.swords: true
mcmmo.perks.xp.10percentboost.taming: true
+ mcmmo.perks.xp.10percentboost.tridents: true
mcmmo.perks.xp.10percentboost.unarmed: true
mcmmo.perks.xp.10percentboost.woodcutting: true
mcmmo.perks.xp.10percentboost.acrobatics:
@@ -1752,6 +1831,9 @@ permissions:
mcmmo.perks.xp.10percentboost.axes:
default: false
description: Multiplies incoming Axes XP by 1.1
+ mcmmo.perks.xp.10percentboost.crossbows:
+ default: false
+ description: Multiplies incoming Crossbows XP by 1.1
mcmmo.perks.xp.10percentboost.excavation:
default: false
description: Multiplies incoming Excavation XP by 1.1
@@ -1776,6 +1858,9 @@ permissions:
mcmmo.perks.xp.10percentboost.taming:
default: false
description: Multiplies incoming Taming XP by 1.1
+ mcmmo.perks.xp.10percentboost.tridents:
+ default: false
+ description: Multiplies incoming Tridents XP by 1.1
mcmmo.perks.xp.10percentboost.unarmed:
default: false
description: Multiplies incoming Unarmed XP by 1.1
@@ -1800,6 +1885,7 @@ permissions:
mcmmo.perks.xp.customboost.alchemy: true
mcmmo.perks.xp.customboost.archery: true
mcmmo.perks.xp.customboost.axes: true
+ mcmmo.perks.xp.customboost.crossbows: true
mcmmo.perks.xp.customboost.excavation: true
mcmmo.perks.xp.customboost.fishing: true
mcmmo.perks.xp.customboost.herbalism: true
@@ -1808,6 +1894,7 @@ permissions:
mcmmo.perks.xp.customboost.smelting: true
mcmmo.perks.xp.customboost.swords: true
mcmmo.perks.xp.customboost.taming: true
+ mcmmo.perks.xp.customboost.tridents: true
mcmmo.perks.xp.customboost.unarmed: true
mcmmo.perks.xp.customboost.woodcutting: true
mcmmo.perks.xp.customboost.acrobatics:
@@ -1822,6 +1909,9 @@ permissions:
mcmmo.perks.xp.customboost.axes:
default: false
description: Multiplies incoming Axes XP by the boost amount defined in the experience config
+ mcmmo.perks.xp.customboost.crossbows:
+ default: false
+ description: Multiplies incoming Crossbows XP by the boost amount defined in the experience config
mcmmo.perks.xp.customboost.excavation:
default: false
description: Multiplies incoming Excavation XP by the boost amount defined in the experience config
@@ -1846,6 +1936,9 @@ permissions:
mcmmo.perks.xp.customboost.taming:
default: false
description: Multiplies incoming Taming XP by the boost amount defined in the experience config
+ mcmmo.perks.xp.customboost.tridents:
+ default: false
+ description: Multiplies incoming Tridents XP by the boost amount defined in the experience config
mcmmo.perks.xp.customboost.unarmed:
default: false
description: Multiplies incoming Unarmed XP by the boost amount defined in the experience config
@@ -1870,6 +1963,7 @@ permissions:
mcmmo.perks.xp.double.alchemy: true
mcmmo.perks.xp.double.archery: true
mcmmo.perks.xp.double.axes: true
+ mcmmo.perks.xp.double.crossbows: true
mcmmo.perks.xp.double.excavation: true
mcmmo.perks.xp.double.fishing: true
mcmmo.perks.xp.double.herbalism: true
@@ -1878,6 +1972,7 @@ permissions:
mcmmo.perks.xp.double.smelting: true
mcmmo.perks.xp.double.swords: true
mcmmo.perks.xp.double.taming: true
+ mcmmo.perks.xp.double.tridents: true
mcmmo.perks.xp.double.unarmed: true
mcmmo.perks.xp.double.woodcutting: true
mcmmo.perks.xp.double.acrobatics:
@@ -1892,6 +1987,9 @@ permissions:
mcmmo.perks.xp.double.axes:
default: false
description: Doubles incoming Axes XP
+ mcmmo.perks.xp.double.crossbows:
+ default: false
+ description: Doubles incoming Crossbows XP
mcmmo.perks.xp.double.excavation:
default: false
description: Doubles incoming Excavation XP
@@ -1916,6 +2014,9 @@ permissions:
mcmmo.perks.xp.double.taming:
default: false
description: Doubles incoming Taming XP
+ mcmmo.perks.xp.double.tridents:
+ default: false
+ description: Doubles incoming Tridents XP
mcmmo.perks.xp.double.unarmed:
default: false
description: Doubles incoming Unarmed XP
@@ -1940,6 +2041,7 @@ permissions:
mcmmo.perks.xp.quadruple.alchemy: true
mcmmo.perks.xp.quadruple.archery: true
mcmmo.perks.xp.quadruple.axes: true
+ mcmmo.perks.xp.quadruple.crossbows: true
mcmmo.perks.xp.quadruple.excavation: true
mcmmo.perks.xp.quadruple.fishing: true
mcmmo.perks.xp.quadruple.herbalism: true
@@ -1948,6 +2050,7 @@ permissions:
mcmmo.perks.xp.quadruple.smelting: true
mcmmo.perks.xp.quadruple.swords: true
mcmmo.perks.xp.quadruple.taming: true
+ mcmmo.perks.xp.quadruple.tridents: true
mcmmo.perks.xp.quadruple.unarmed: true
mcmmo.perks.xp.quadruple.woodcutting: true
mcmmo.perks.xp.quadruple.acrobatics:
@@ -1962,6 +2065,9 @@ permissions:
mcmmo.perks.xp.quadruple.axes:
default: false
description: Quadruples incoming Axes XP
+ mcmmo.perks.xp.quadruple.crossbows:
+ default: false
+ description: Quadruples incoming Crossbows XP
mcmmo.perks.xp.quadruple.excavation:
default: false
description: Quadruples incoming Excavation XP
@@ -1986,6 +2092,9 @@ permissions:
mcmmo.perks.xp.quadruple.taming:
default: false
description: Quadruples incoming Taming XP
+ mcmmo.perks.xp.quadruple.tridents:
+ default: false
+ description: Quadruples incoming Tridents XP
mcmmo.perks.xp.quadruple.unarmed:
default: false
description: Quadruples incoming Unarmed XP
@@ -2010,6 +2119,7 @@ permissions:
mcmmo.perks.xp.triple.alchemy: true
mcmmo.perks.xp.triple.archery: true
mcmmo.perks.xp.triple.axes: true
+ mcmmo.perks.xp.triple.crossbows: true
mcmmo.perks.xp.triple.excavation: true
mcmmo.perks.xp.triple.fishing: true
mcmmo.perks.xp.triple.herbalism: true
@@ -2018,6 +2128,7 @@ permissions:
mcmmo.perks.xp.triple.smelting: true
mcmmo.perks.xp.triple.swords: true
mcmmo.perks.xp.triple.taming: true
+ mcmmo.perks.xp.triple.tridents: true
mcmmo.perks.xp.triple.unarmed: true
mcmmo.perks.xp.triple.woodcutting: true
mcmmo.perks.xp.triple.acrobatics:
@@ -2032,6 +2143,9 @@ permissions:
mcmmo.perks.xp.triple.axes:
default: false
description: Triples incoming Axes XP
+ mcmmo.perks.xp.triple.crossbows:
+ default: false
+ description: Triples incoming Crossbows XP
mcmmo.perks.xp.triple.excavation:
default: false
description: Triples incoming Excavation XP
@@ -2056,6 +2170,9 @@ permissions:
mcmmo.perks.xp.triple.taming:
default: false
description: Triples incoming Taming XP
+ mcmmo.perks.xp.triple.tridents:
+ default: false
+ description: Triples incoming Tridents XP
mcmmo.perks.xp.triple.unarmed:
default: false
description: Triples incoming Unarmed XP
@@ -2091,6 +2208,8 @@ permissions:
mcmmo.skills.taming: true
mcmmo.skills.unarmed: true
mcmmo.skills.woodcutting: true
+ mcmmo.skills.crossbows: true
+ mcmmo.skills.tridents: true
mcmmo.skills.acrobatics:
description: Allows access to the Acrobatics skill
children:
@@ -2111,6 +2230,11 @@ permissions:
children:
mcmmo.ability.axes.all: true
mcmmo.commands.axes: true
+ mcmmo.skills.crossbows:
+ description: Allows access to the Crossbows skill
+ children:
+ mcmmo.ability.crossbows.all: true
+ mcmmo.commands.crossbows: true
mcmmo.skills.excavation:
description: Allows access to the Excavation skill
children:
@@ -2156,6 +2280,11 @@ permissions:
children:
mcmmo.ability.taming.all: true
mcmmo.commands.taming: true
+ mcmmo.skills.tridents:
+ description: Allows access to the Tridents skill
+ children:
+ mcmmo.ability.tridents.all: true
+ mcmmo.commands.tridents: true
mcmmo.skills.unarmed:
description: Allows access to the Unarmed skill
children:
@@ -2169,16 +2298,3 @@ permissions:
mcmmo.showversion:
default: true
description: Show mcMMO version number in /mcmmo and motd
- mcmmo.tools.*:
- default: false
- description: Implies all mcmmo.tools permissions.
- children:
- mcmmo.tools.all: true
- mcmmo.tools.all:
- default: false
- description: Implies all mcmmo.tools permissions.
- children:
- mcmmo.tools.updatecheck: true
- mcmmo.tools.updatecheck:
- default: false
- description: Notifies admins if there is a new version of mcMMO available
diff --git a/src/main/resources/skillranks.yml b/src/main/resources/skillranks.yml
index 1a305921b..590ddde11 100644
--- a/src/main/resources/skillranks.yml
+++ b/src/main/resources/skillranks.yml
@@ -201,6 +201,129 @@ Axes:
Rank_2: 100
Rank_3: 150
Rank_4: 200
+Crossbows:
+ TrickShot:
+ Standard:
+ Rank_1: 5
+ Rank_2: 20
+ Rank_3: 40
+ RetroMode:
+ Rank_1: 50
+ Rank_2: 200
+ Rank_3: 400
+ PoweredShot:
+ Standard:
+ Rank_1: 1
+ Rank_2: 10
+ Rank_3: 15
+ Rank_4: 20
+ Rank_5: 25
+ Rank_6: 30
+ Rank_7: 35
+ Rank_8: 40
+ Rank_9: 45
+ Rank_10: 50
+ Rank_11: 55
+ Rank_12: 60
+ Rank_13: 65
+ Rank_14: 70
+ Rank_15: 75
+ Rank_16: 80
+ Rank_17: 85
+ Rank_18: 90
+ Rank_19: 95
+ Rank_20: 100
+ RetroMode:
+ Rank_1: 1
+ Rank_2: 100
+ Rank_3: 150
+ Rank_4: 200
+ Rank_5: 250
+ Rank_6: 300
+ Rank_7: 350
+ Rank_8: 400
+ Rank_9: 450
+ Rank_10: 500
+ Rank_11: 550
+ Rank_12: 600
+ Rank_13: 650
+ Rank_14: 700
+ Rank_15: 750
+ Rank_16: 800
+ Rank_17: 850
+ Rank_18: 900
+ Rank_19: 950
+ Rank_20: 1000
+ CrossbowsLimitBreak:
+ Standard:
+ Rank_1: 10
+ Rank_2: 20
+ Rank_3: 30
+ Rank_4: 40
+ Rank_5: 50
+ Rank_6: 60
+ Rank_7: 70
+ Rank_8: 80
+ Rank_9: 90
+ Rank_10: 100
+ RetroMode:
+ Rank_1: 100
+ Rank_2: 200
+ Rank_3: 300
+ Rank_4: 400
+ Rank_5: 500
+ Rank_6: 600
+ Rank_7: 700
+ Rank_8: 800
+ Rank_9: 900
+ Rank_10: 1000
+Tridents:
+ TridentsLimitBreak:
+ Standard:
+ Rank_1: 10
+ Rank_2: 20
+ Rank_3: 30
+ Rank_4: 40
+ Rank_5: 50
+ Rank_6: 60
+ Rank_7: 70
+ Rank_8: 80
+ Rank_9: 90
+ Rank_10: 100
+ RetroMode:
+ Rank_1: 100
+ Rank_2: 200
+ Rank_3: 300
+ Rank_4: 400
+ Rank_5: 500
+ Rank_6: 600
+ Rank_7: 700
+ Rank_8: 800
+ Rank_9: 900
+ Rank_10: 1000
+ Impale:
+ Standard:
+ Rank_1: 5
+ Rank_2: 15
+ Rank_3: 25
+ Rank_4: 35
+ Rank_5: 45
+ Rank_6: 55
+ Rank_7: 65
+ Rank_8: 75
+ Rank_9: 85
+ Rank_10: 100
+ RetroMode:
+ Rank_1: 50
+ Rank_2: 150
+ Rank_3: 250
+ Rank_4: 350
+ Rank_5: 450
+ Rank_6: 550
+ Rank_7: 650
+ Rank_8: 750
+ Rank_9: 850
+ Rank_10: 1000
Taming:
BeastLore:
Standard:
@@ -321,6 +444,11 @@ Salvage:
Rank_7: 850
Rank_8: 1000
Mining:
+ MotherLode:
+ Standard:
+ Rank_1: 100
+ RetroMode:
+ Rank_1: 1000
DoubleDrops:
Standard:
Rank_1: 1
@@ -368,6 +496,11 @@ Herbalism:
Rank_1: 1
RetroMode:
Rank_1: 1
+ VerdantBounty:
+ Standard:
+ Rank_1: 100
+ RetroMode:
+ Rank_1: 1000
GreenTerra:
Standard:
Rank_1: 5
@@ -398,6 +531,11 @@ Herbalism:
Rank_4: 800
Rank_5: 1000
Fishing:
+ Mastery:
+ Standard:
+ Rank_1: 100
+ RetroMode:
+ Rank_1: 1000
MagicHunter:
Standard:
Rank_1: 20
@@ -637,6 +775,11 @@ Woodcutting:
Rank_1: 1
RetroMode:
Rank_1: 1
+ CleanCuts:
+ Standard:
+ Rank_1: 100
+ RetroMode:
+ Rank_1: 1000
KnockOnWood:
Standard:
Rank_1: 30
diff --git a/src/test/java/com/gmail/nossr50/MMOTestEnvironment.java b/src/test/java/com/gmail/nossr50/MMOTestEnvironment.java
new file mode 100644
index 000000000..d995ed569
--- /dev/null
+++ b/src/test/java/com/gmail/nossr50/MMOTestEnvironment.java
@@ -0,0 +1,216 @@
+package com.gmail.nossr50;
+
+import com.gmail.nossr50.api.exceptions.InvalidSkillException;
+import com.gmail.nossr50.config.AdvancedConfig;
+import com.gmail.nossr50.config.ChatConfig;
+import com.gmail.nossr50.config.GeneralConfig;
+import com.gmail.nossr50.config.RankConfig;
+import com.gmail.nossr50.config.experience.ExperienceConfig;
+import com.gmail.nossr50.config.party.PartyConfig;
+import com.gmail.nossr50.datatypes.player.McMMOPlayer;
+import com.gmail.nossr50.datatypes.player.PlayerProfile;
+import com.gmail.nossr50.datatypes.skills.PrimarySkillType;
+import com.gmail.nossr50.datatypes.skills.SubSkillType;
+import com.gmail.nossr50.util.*;
+import com.gmail.nossr50.util.blockmeta.ChunkManager;
+import com.gmail.nossr50.util.player.UserManager;
+import com.gmail.nossr50.util.skills.RankUtils;
+import com.gmail.nossr50.util.skills.SkillTools;
+import org.bukkit.Location;
+import org.bukkit.Material;
+import org.bukkit.Server;
+import org.bukkit.World;
+import org.bukkit.entity.Player;
+import org.bukkit.inventory.ItemStack;
+import org.bukkit.inventory.PlayerInventory;
+import org.bukkit.plugin.PluginManager;
+import org.mockito.MockedStatic;
+import org.mockito.Mockito;
+
+import java.util.UUID;
+import java.util.logging.Logger;
+
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.Mockito.when;
+
+public abstract class MMOTestEnvironment {
+ protected MockedStatic mockedMcMMO;
+ protected MockedStatic mockedChatConfig;
+ protected MockedStatic experienceConfig;
+ protected MockedStatic mockedPermissions;
+ protected MockedStatic mockedRankUtils;
+ protected MockedStatic mockedUserManager;
+ protected MockedStatic mockedMisc;
+ protected MockedStatic mockedSkillTools;
+ protected MockedStatic mockedEventUtils;
+ protected TransientEntityTracker transientEntityTracker;
+ protected AdvancedConfig advancedConfig;
+ protected PartyConfig partyConfig;
+ protected GeneralConfig generalConfig;
+ protected RankConfig rankConfig;
+ protected SkillTools skillTools;
+ protected Server server;
+ protected PluginManager pluginManager;
+ protected World world;
+
+ /* Mocks */
+ protected Player player;
+
+ protected UUID playerUUID = UUID.randomUUID();
+ protected ItemStack itemInMainHand;
+
+ protected PlayerInventory playerInventory;
+ protected PlayerProfile playerProfile;
+ protected McMMOPlayer mmoPlayer;
+ protected String playerName = "testPlayer";
+
+ protected ChunkManager chunkManager;
+ protected MaterialMapStore materialMapStore;
+
+ protected void mockBaseEnvironment(Logger logger) throws InvalidSkillException {
+ mockedMcMMO = Mockito.mockStatic(mcMMO.class);
+ mcMMO.p = Mockito.mock(mcMMO.class);
+ when(mcMMO.p.getLogger()).thenReturn(logger);
+
+ // place store
+ chunkManager = Mockito.mock(ChunkManager.class);
+ when(mcMMO.getPlaceStore()).thenReturn(chunkManager);
+
+ // shut off mod manager for woodcutting
+ when(mcMMO.getModManager()).thenReturn(Mockito.mock(ModManager.class));
+ when(mcMMO.getModManager().isCustomLog(any())).thenReturn(false);
+
+ // chat config
+ mockedChatConfig = Mockito.mockStatic(ChatConfig.class);
+ when(ChatConfig.getInstance()).thenReturn(Mockito.mock(ChatConfig.class));
+
+ // general config
+ mockGeneralConfig();
+
+ // party config
+ mockPartyConfig();
+
+ // rank config
+ mockRankConfig();
+
+ // wire advanced config
+ mockAdvancedConfig();
+
+ // wire experience config
+ mockExperienceConfig();
+
+ // wire skill tools
+ this.skillTools = new SkillTools(mcMMO.p);
+ when(mcMMO.p.getSkillTools()).thenReturn(skillTools);
+
+ this.transientEntityTracker = new TransientEntityTracker();
+ when(mcMMO.getTransientEntityTracker()).thenReturn(transientEntityTracker);
+
+ mockPermissions();
+
+ mockedRankUtils = Mockito.mockStatic(RankUtils.class);
+
+ // wire server
+ this.server = Mockito.mock(Server.class);
+ when(mcMMO.p.getServer()).thenReturn(server);
+
+ // wire plugin manager
+ this.pluginManager = Mockito.mock(PluginManager.class);
+ when(server.getPluginManager()).thenReturn(pluginManager);
+
+ // wire world
+ this.world = Mockito.mock(World.class);
+
+ // wire Misc
+ this.mockedMisc = Mockito.mockStatic(Misc.class);
+ when(Misc.getBlockCenter(any())).thenReturn(new Location(world, 0, 0, 0));
+
+ // setup player and player related mocks after everything else
+ this.player = Mockito.mock(Player.class);
+ when(player.getUniqueId()).thenReturn(playerUUID);
+
+ // wire inventory
+ this.playerInventory = Mockito.mock(PlayerInventory.class);
+ when(player.getInventory()).thenReturn(playerInventory);
+
+ // PlayerProfile and McMMOPlayer are partially mocked
+ playerProfile = new PlayerProfile("testPlayer", player.getUniqueId(), 0);
+ mmoPlayer = Mockito.spy(new McMMOPlayer(player, playerProfile));
+
+ // wire user manager
+ this.mockedUserManager = Mockito.mockStatic(UserManager.class);
+ when(UserManager.getPlayer(player)).thenReturn(mmoPlayer);
+
+ this.materialMapStore = new MaterialMapStore();
+ when(mcMMO.getMaterialMapStore()).thenReturn(materialMapStore);
+ }
+
+ private void mockPermissions() {
+ mockedPermissions = Mockito.mockStatic(Permissions.class);
+ when(Permissions.isSubSkillEnabled(any(Player.class), any(SubSkillType.class))).thenReturn(true);
+ when(Permissions.canUseSubSkill(any(Player.class), any(SubSkillType.class))).thenReturn(true);
+ when(Permissions.isSubSkillEnabled(any(Player.class), any(SubSkillType.class))).thenReturn(true);
+ when(Permissions.canUseSubSkill(any(Player.class), any(SubSkillType.class))).thenReturn(true);
+ when(Permissions.lucky(player, PrimarySkillType.WOODCUTTING)).thenReturn(false); // player is not lucky
+ }
+
+ private void mockRankConfig() {
+ rankConfig = Mockito.mock(RankConfig.class);
+ }
+
+ private void mockAdvancedConfig() {
+ this.advancedConfig = Mockito.mock(AdvancedConfig.class);
+ when(mcMMO.p.getAdvancedConfig()).thenReturn(advancedConfig);
+ }
+
+ private void mockGeneralConfig() {
+ generalConfig = Mockito.mock(GeneralConfig.class);
+ when(generalConfig.getTreeFellerThreshold()).thenReturn(100);
+ when(generalConfig.getDoubleDropsEnabled(PrimarySkillType.WOODCUTTING, Material.OAK_LOG)).thenReturn(true);
+ when(generalConfig.getLocale()).thenReturn("en_US");
+ when(mcMMO.p.getGeneralConfig()).thenReturn(generalConfig);
+ }
+
+ private void mockPartyConfig() {
+ partyConfig = Mockito.mock(PartyConfig.class);
+ when(partyConfig.isPartyEnabled()).thenReturn(false);
+ when(mcMMO.p.getPartyConfig()).thenReturn(partyConfig);
+ }
+
+ private void mockExperienceConfig() {
+ experienceConfig = Mockito.mockStatic(ExperienceConfig.class);
+
+ when(ExperienceConfig.getInstance()).thenReturn(Mockito.mock(ExperienceConfig.class));
+
+ // Combat
+ when(ExperienceConfig.getInstance().getCombatXP("Cow")).thenReturn(1D);
+ }
+
+ protected void cleanupBaseEnvironment() {
+ // Clean up resources here if needed.
+ if (mockedMcMMO != null) {
+ mockedMcMMO.close();
+ }
+ if (experienceConfig != null) {
+ experienceConfig.close();
+ }
+ if (mockedChatConfig != null) {
+ mockedChatConfig.close();
+ }
+ if (mockedPermissions != null) {
+ mockedPermissions.close();
+ }
+ if (mockedRankUtils != null) {
+ mockedRankUtils.close();
+ }
+ if (mockedUserManager != null) {
+ mockedUserManager.close();
+ }
+ if (mockedMisc != null) {
+ mockedMisc.close();
+ }
+ if (mockedEventUtils != null) {
+ mockedEventUtils.close();
+ }
+ }
+}
diff --git a/src/test/java/com/gmail/nossr50/database/FlatFileDatabaseManagerTest.java b/src/test/java/com/gmail/nossr50/database/FlatFileDatabaseManagerTest.java
index f626002c1..23463cbdb 100644
--- a/src/test/java/com/gmail/nossr50/database/FlatFileDatabaseManagerTest.java
+++ b/src/test/java/com/gmail/nossr50/database/FlatFileDatabaseManagerTest.java
@@ -31,7 +31,6 @@ import java.util.logging.Logger;
import static org.junit.jupiter.api.Assertions.*;
import static org.mockito.Mockito.mock;
-//This class uses JUnit5/Jupiter
class FlatFileDatabaseManagerTest {
public static final @NotNull String TEST_FILE_NAME = "test.mcmmo.users";
@@ -39,7 +38,7 @@ class FlatFileDatabaseManagerTest {
public static final @NotNull String BAD_DATA_FILE_LINE_TWENTY_THREE = "nossr51:baddata:::baddata:baddata:640:baddata:1000:1000:1000:baddata:baddata:baddata:baddata:16:0:500:20273:0:0:0:0::1000:0:0:baddata:1593543012:0:0:0:0::1000:0:0:baddata:IGNORED:1000:0:588fe472-1c82-4c4e-9aa1-7eefccb277e3:1:0:";
public static final @NotNull String DB_BADDATA = "baddatadb.users";
public static final @NotNull String DB_HEALTHY = "healthydb.users";
- public static final @NotNull String HEALTHY_DB_LINE_1 = "nossr50:1:IGNORED:IGNORED:10:2:20:3:4:5:6:7:8:9:10:30:40:50:60:70:80:90:100:IGNORED:11:110:111:222:333:444:555:666:777:IGNORED:12:120:888:IGNORED:HEARTS:13:130:588fe472-1c82-4c4e-9aa1-7eefccb277e3:1111:999:2020:";
+ public static final @NotNull String HEALTHY_DB_LINE_1 = "nossr50:1:IGNORED:IGNORED:10:2:20:3:4:5:6:7:8:9:10:30:40:50:60:70:80:90:100:IGNORED:11:110:111:222:333:444:555:666:777:IGNORED:12:120:888:IGNORED:HEARTS:13:130:588fe472-1c82-4c4e-9aa1-7eefccb277e3:1111:999:2020:140:14:150:15:1111:2222:3333";
public static final @NotNull String HEALTHY_DB_LINE_ONE_UUID_STR = "588fe472-1c82-4c4e-9aa1-7eefccb277e3";
public static final String DB_MISSING_LAST_LOGIN = "missinglastlogin.users";
public static final String LINE_TWO_FROM_MISSING_DB = "nossr50:1:IGNORED:IGNORED:10:2:20:3:4:5:6:7:8:9:10:30:40:50:60:70:80:90:100:IGNORED:11:110:111:222:333:444:555:666:777:IGNORED:12:120:888:0:HEARTS:13:130:588fe472-1c82-4c4e-9aa1-7eefccb277e3:1111:999:";
@@ -52,16 +51,19 @@ class FlatFileDatabaseManagerTest {
int expectedLvlMining = 1, expectedLvlWoodcutting = 2, expectedLvlRepair = 3,
expectedLvlUnarmed = 4, expectedLvlHerbalism = 5, expectedLvlExcavation = 6,
expectedLvlArchery = 7, expectedLvlSwords = 8, expectedLvlAxes = 9, expectedLvlAcrobatics = 10,
- expectedLvlTaming = 11, expectedLvlFishing = 12, expectedLvlAlchemy = 13;
+ expectedLvlTaming = 11, expectedLvlFishing = 12, expectedLvlAlchemy = 13, expectedLvlCrossbows = 14,
+ expectedLvlTridents = 15;
float expectedExpMining = 10, expectedExpWoodcutting = 20, expectedExpRepair = 30,
expectedExpUnarmed = 40, expectedExpHerbalism = 50, expectedExpExcavation = 60,
expectedExpArchery = 70, expectedExpSwords = 80, expectedExpAxes = 90, expectedExpAcrobatics = 100,
- expectedExpTaming = 110, expectedExpFishing = 120, expectedExpAlchemy = 130;
+ expectedExpTaming = 110, expectedExpFishing = 120, expectedExpAlchemy = 130, expectedExpCrossbows = 140,
+ expectedExpTridents = 150;
long expectedBerserkCd = 111, expectedGigaDrillBreakerCd = 222, expectedTreeFellerCd = 333,
expectedGreenTerraCd = 444, expectedSerratedStrikesCd = 555, expectedSkullSplitterCd = 666,
- expectedSuperBreakerCd = 777, expectedBlastMiningCd = 888, expectedChimaeraWingCd = 999;
+ expectedSuperBreakerCd = 777, expectedBlastMiningCd = 888, expectedChimaeraWingCd = 999,
+ expectedSuperShotgunCd = 1111, expectedTridentSuperCd = 2222, expectedExplosiveShotCd = 3333;
int expectedScoreboardTips = 1111;
Long expectedLastLogin = 2020L;
@@ -226,7 +228,6 @@ class FlatFileDatabaseManagerTest {
logger.info("File Path: "+healthyDB.getAbsolutePath());
assertArrayEquals(HEALTHY_DB_LINE_1.split(":"), dataFromFile.get(0));
assertEquals(dataFromFile.get(0)[FlatFileDatabaseManager.UUID_INDEX], HEALTHY_DB_LINE_ONE_UUID_STR);
- UUID healthDBEntryOneUUID = UUID.fromString(HEALTHY_DB_LINE_ONE_UUID_STR);
db = new FlatFileDatabaseManager(healthyDB, logger, PURGE_TIME, 0, true);
List flagsFound = db.checkFileHealthAndStructure();
@@ -451,14 +452,13 @@ class FlatFileDatabaseManagerTest {
if(SkillTools.isChildSkill(primarySkillType))
continue;
-// logger.info("Checking expected values for: "+primarySkillType);
-// logger.info("Profile Level Value: "+profile.getSkillLevel(primarySkillType));
-// logger.info("Expected Lvl Value: "+getExpectedLevelHealthyDBEntryOne(primarySkillType));
-// logger.info("Profile Exp Value: "+profile.getSkillXpLevelRaw(primarySkillType));
-// logger.info("Expected Exp Value: "+getExpectedExperienceHealthyDBEntryOne(primarySkillType));
+ int expectedLevelHealthyDBEntryOne = getExpectedLevelHealthyDBEntryOne(primarySkillType);
+ int skillLevel = profile.getSkillLevel(primarySkillType);
+ assertEquals(expectedLevelHealthyDBEntryOne, skillLevel);
- assertEquals(getExpectedLevelHealthyDBEntryOne(primarySkillType), profile.getSkillLevel(primarySkillType));
- assertEquals(getExpectedExperienceHealthyDBEntryOne(primarySkillType), profile.getSkillXpLevelRaw(primarySkillType), 0);
+ float expectedExperienceHealthyDBEntryOne = getExpectedExperienceHealthyDBEntryOne(primarySkillType);
+ float skillXpLevelRaw = profile.getSkillXpLevelRaw(primarySkillType);
+ assertEquals(expectedExperienceHealthyDBEntryOne, skillXpLevelRaw, 0);
}
//Check the other things
@@ -472,29 +472,24 @@ class FlatFileDatabaseManagerTest {
}
private long getExpectedSuperAbilityDATS(@NotNull SuperAbilityType superAbilityType) {
- switch(superAbilityType) {
- case BERSERK:
- return expectedBerserkCd;
- case SUPER_BREAKER:
- return expectedSuperBreakerCd;
- case GIGA_DRILL_BREAKER:
- return expectedGigaDrillBreakerCd;
- case GREEN_TERRA:
- return expectedGreenTerraCd;
- case SKULL_SPLITTER:
- return expectedSkullSplitterCd;
- case TREE_FELLER:
- return expectedTreeFellerCd;
- case SERRATED_STRIKES:
- return expectedSerratedStrikesCd;
- case BLAST_MINING:
- return expectedBlastMiningCd;
- }
+ return switch (superAbilityType) {
+ case BERSERK -> expectedBerserkCd;
+ case SUPER_BREAKER -> expectedSuperBreakerCd;
+ case GIGA_DRILL_BREAKER -> expectedGigaDrillBreakerCd;
+ case GREEN_TERRA -> expectedGreenTerraCd;
+ case SKULL_SPLITTER -> expectedSkullSplitterCd;
+ case SUPER_SHOTGUN -> expectedSuperShotgunCd;
+ case TREE_FELLER -> expectedTreeFellerCd;
+ case SERRATED_STRIKES -> expectedSerratedStrikesCd;
+ case BLAST_MINING -> expectedBlastMiningCd;
+ case TRIDENTS_SUPER_ABILITY -> expectedTridentSuperCd;
+ case EXPLOSIVE_SHOT -> expectedExplosiveShotCd;
+ default -> throw new RuntimeException("Values not defined for super ability please add " +
+ "values for " + superAbilityType.toString() + " to the test");
+ };
- return -1;
}
- //TODO: Why is this stuff a float?
private float getExpectedExperienceHealthyDBEntryOne(@NotNull PrimarySkillType primarySkillType) {
switch(primarySkillType) {
case ACROBATICS:
@@ -505,6 +500,8 @@ class FlatFileDatabaseManagerTest {
return expectedExpArchery;
case AXES:
return expectedExpAxes;
+ case CROSSBOWS:
+ return expectedExpCrossbows;
case EXCAVATION:
return expectedExpExcavation;
case FISHING:
@@ -522,13 +519,15 @@ class FlatFileDatabaseManagerTest {
return expectedExpSwords;
case TAMING:
return expectedExpTaming;
+ case TRIDENTS:
+ return expectedExpTridents;
case UNARMED:
return expectedExpUnarmed;
case WOODCUTTING:
return expectedExpWoodcutting;
}
- return -1;
+ throw new RuntimeException("Values for skill not defined, please add values for " + primarySkillType.toString() + " to the test");
}
private int getExpectedLevelHealthyDBEntryOne(@NotNull PrimarySkillType primarySkillType) {
@@ -541,6 +540,8 @@ class FlatFileDatabaseManagerTest {
return expectedLvlArchery;
case AXES:
return expectedLvlAxes;
+ case CROSSBOWS:
+ return expectedLvlCrossbows;
case EXCAVATION:
return expectedLvlExcavation;
case FISHING:
@@ -558,13 +559,15 @@ class FlatFileDatabaseManagerTest {
return expectedLvlSwords;
case TAMING:
return expectedLvlTaming;
+ case TRIDENTS:
+ return expectedLvlTridents;
case UNARMED:
return expectedLvlUnarmed;
case WOODCUTTING:
return expectedLvlWoodcutting;
}
- return -1;
+ throw new RuntimeException("Values for skill not defined, please add values for " + primarySkillType.toString() + " to the test");
}
@Test
diff --git a/src/test/java/com/gmail/nossr50/database/SQLDatabaseManagerTest.java b/src/test/java/com/gmail/nossr50/database/SQLDatabaseManagerTest.java
new file mode 100644
index 000000000..959777ab9
--- /dev/null
+++ b/src/test/java/com/gmail/nossr50/database/SQLDatabaseManagerTest.java
@@ -0,0 +1,245 @@
+//package com.gmail.nossr50.database;
+//
+//import com.gmail.nossr50.config.AdvancedConfig;
+//import com.gmail.nossr50.config.GeneralConfig;
+//import com.gmail.nossr50.datatypes.MobHealthbarType;
+//import com.gmail.nossr50.datatypes.player.PlayerProfile;
+//import com.gmail.nossr50.datatypes.skills.PrimarySkillType;
+//import com.gmail.nossr50.mcMMO;
+//import com.gmail.nossr50.util.compat.CompatibilityManager;
+//import com.gmail.nossr50.util.platform.MinecraftGameVersion;
+//import com.gmail.nossr50.util.skills.SkillTools;
+//import com.gmail.nossr50.util.upgrade.UpgradeManager;
+//import org.bukkit.entity.Player;
+//import org.jetbrains.annotations.NotNull;
+//import org.junit.jupiter.api.*;
+//import org.mockito.MockedStatic;
+//import org.mockito.Mockito;
+//
+//import java.util.logging.Logger;
+//
+//import static org.junit.jupiter.api.Assertions.*;
+//import static org.mockito.ArgumentMatchers.any;
+//import static org.mockito.Mockito.when;
+//
+//class SQLDatabaseManagerTest {
+// private final static @NotNull Logger logger = Logger.getLogger(Logger.GLOBAL_LOGGER_NAME);
+// static MockedStatic mockedMcMMO;
+// SQLDatabaseManager sqlDatabaseManager;
+// static GeneralConfig generalConfig;
+// static AdvancedConfig advancedConfig;
+// static UpgradeManager upgradeManager;
+// static CompatibilityManager compatibilityManager;
+// static SkillTools skillTools;
+//
+// @BeforeAll
+// static void setUpAll() {
+// // stub mcMMO.p
+// mockedMcMMO = Mockito.mockStatic(mcMMO.class);
+// mcMMO.p = Mockito.mock(mcMMO.class);
+// when(mcMMO.p.getLogger()).thenReturn(logger);
+//
+// // general config mock
+// mockGeneralConfig();
+//
+// // advanced config mock
+// advancedConfig = Mockito.mock(AdvancedConfig.class);
+// when(mcMMO.p.getAdvancedConfig()).thenReturn(advancedConfig);
+//
+// // starting level
+// when(mcMMO.p.getAdvancedConfig().getStartingLevel()).thenReturn(0);
+//
+// // wire skill tools
+// skillTools = new SkillTools(mcMMO.p);
+// when(mcMMO.p.getSkillTools()).thenReturn(skillTools);
+//
+// // compatibility manager mock
+// compatibilityManager = Mockito.mock(CompatibilityManager.class);
+// when(mcMMO.getCompatibilityManager()).thenReturn(compatibilityManager);
+// when(compatibilityManager.getMinecraftGameVersion()).thenReturn(new MinecraftGameVersion(1, 20, 4));
+//
+// // upgrade manager mock
+// upgradeManager = Mockito.mock(UpgradeManager.class);
+// when(mcMMO.getUpgradeManager()).thenReturn(upgradeManager);
+//
+// // don't trigger upgrades
+// when(mcMMO.getUpgradeManager().shouldUpgrade(any())).thenReturn(false);
+// }
+//
+// private static void mockGeneralConfig() {
+// generalConfig = Mockito.mock(GeneralConfig.class);
+// when(generalConfig.getLocale()).thenReturn("en_US");
+// when(mcMMO.p.getGeneralConfig()).thenReturn(generalConfig);
+//
+// // max pool size
+// when(mcMMO.p.getGeneralConfig().getMySQLMaxPoolSize(SQLDatabaseManager.PoolIdentifier.MISC))
+// .thenReturn(10);
+// when(mcMMO.p.getGeneralConfig().getMySQLMaxPoolSize(SQLDatabaseManager.PoolIdentifier.LOAD))
+// .thenReturn(20);
+// when(mcMMO.p.getGeneralConfig().getMySQLMaxPoolSize(SQLDatabaseManager.PoolIdentifier.SAVE))
+// .thenReturn(20);
+//
+// // max connections
+// when(mcMMO.p.getGeneralConfig().getMySQLMaxConnections(SQLDatabaseManager.PoolIdentifier.MISC))
+// .thenReturn(30);
+// when(mcMMO.p.getGeneralConfig().getMySQLMaxConnections(SQLDatabaseManager.PoolIdentifier.LOAD))
+// .thenReturn(30);
+// when(mcMMO.p.getGeneralConfig().getMySQLMaxConnections(SQLDatabaseManager.PoolIdentifier.SAVE))
+// .thenReturn(30);
+//
+// // table prefix
+// when(mcMMO.p.getGeneralConfig().getMySQLTablePrefix()).thenReturn("mcmmo_");
+//
+// // public key retrieval
+// when(mcMMO.p.getGeneralConfig().getMySQLPublicKeyRetrieval()).thenReturn(true);
+//
+// // debug
+// when(mcMMO.p.getGeneralConfig().getMySQLDebug()).thenReturn(true);
+//
+// // use mysql
+// when(mcMMO.p.getGeneralConfig().getUseMySQL()).thenReturn(true);
+//
+// // use ssl
+// when(mcMMO.p.getGeneralConfig().getMySQLSSL()).thenReturn(true);
+//
+// // username
+// when(mcMMO.p.getGeneralConfig().getMySQLUserName()).thenReturn("sa");
+//
+// // password
+// when(mcMMO.p.getGeneralConfig().getMySQLUserPassword()).thenReturn("");
+//
+// // host
+// when(mcMMO.p.getGeneralConfig().getMySQLServerName()).thenReturn("localhost");
+//
+// // unused mob health bar thingy
+// when(mcMMO.p.getGeneralConfig().getMobHealthbarDefault()).thenReturn(MobHealthbarType.HEARTS);
+// }
+//
+// @BeforeEach
+// void setUp() {
+// assertNull(sqlDatabaseManager);
+// sqlDatabaseManager = new SQLDatabaseManager(logger, "org.h2.Driver", true);
+// }
+//
+// @AfterEach
+// void tearDown() {
+// sqlDatabaseManager = null;
+// }
+//
+// @AfterAll
+// static void tearDownAll() {
+// mockedMcMMO.close();
+// }
+//
+// @Test
+// void testGetConnectionMisc() throws Exception {
+// assertNotNull(sqlDatabaseManager.getConnection(SQLDatabaseManager.PoolIdentifier.MISC));
+// }
+//
+// @Test
+// void testGetConnectionLoad() throws Exception {
+// assertNotNull(sqlDatabaseManager.getConnection(SQLDatabaseManager.PoolIdentifier.LOAD));
+// }
+//
+// @Test
+// void testGetConnectionSave() throws Exception {
+// assertNotNull(sqlDatabaseManager.getConnection(SQLDatabaseManager.PoolIdentifier.SAVE));
+// }
+//
+// @Test
+// void testNewUser() {
+// Player player = Mockito.mock(Player.class);
+// when(player.getUniqueId()).thenReturn(java.util.UUID.randomUUID());
+// when(player.getName()).thenReturn("nossr50");
+// sqlDatabaseManager.newUser(player);
+// }
+//
+// @Test
+// void testNewUserGetSkillLevel() {
+// Player player = Mockito.mock(Player.class);
+// when(player.getUniqueId()).thenReturn(java.util.UUID.randomUUID());
+// when(player.getName()).thenReturn("nossr50");
+// PlayerProfile playerProfile = sqlDatabaseManager.newUser(player);
+//
+// for (PrimarySkillType primarySkillType : PrimarySkillType.values()) {
+// assertEquals(0, playerProfile.getSkillLevel(primarySkillType));
+// }
+// }
+//
+// @Test
+// void testNewUserGetSkillXpLevel() {
+// Player player = Mockito.mock(Player.class);
+// when(player.getUniqueId()).thenReturn(java.util.UUID.randomUUID());
+// when(player.getName()).thenReturn("nossr50");
+// PlayerProfile playerProfile = sqlDatabaseManager.newUser(player);
+//
+// for (PrimarySkillType primarySkillType : PrimarySkillType.values()) {
+// assertEquals(0, playerProfile.getSkillXpLevel(primarySkillType));
+// }
+// }
+//
+// @Test
+// void testSaveSkillLevelValues() {
+// Player player = Mockito.mock(Player.class);
+// when(player.getUniqueId()).thenReturn(java.util.UUID.randomUUID());
+// when(player.getName()).thenReturn("nossr50");
+// PlayerProfile playerProfile = sqlDatabaseManager.newUser(player);
+//
+// // Validate values are starting from zero
+// for (PrimarySkillType primarySkillType : PrimarySkillType.values()) {
+// assertEquals(0, playerProfile.getSkillXpLevel(primarySkillType));
+// }
+//
+// // Change values
+// for (PrimarySkillType primarySkillType : PrimarySkillType.values()) {
+// playerProfile.modifySkill(primarySkillType, 1 + primarySkillType.ordinal());
+// }
+//
+// boolean saveSuccess = sqlDatabaseManager.saveUser(playerProfile);
+// assertTrue(saveSuccess);
+//
+// PlayerProfile retrievedUser = sqlDatabaseManager.loadPlayerProfile(player.getName());
+//
+// // Check that values got saved
+// for (PrimarySkillType primarySkillType : PrimarySkillType.values()) {
+// if (primarySkillType == PrimarySkillType.SALVAGE || primarySkillType == PrimarySkillType.SMELTING) {
+// // Child skills are not saved, but calculated
+// continue;
+// }
+//
+// assertEquals(1 + primarySkillType.ordinal(), retrievedUser.getSkillLevel(primarySkillType));
+// }
+// }
+//
+// @Test
+// void testSaveSkillXpValues() {
+// Player player = Mockito.mock(Player.class);
+// when(player.getUniqueId()).thenReturn(java.util.UUID.randomUUID());
+// when(player.getName()).thenReturn("nossr50");
+// PlayerProfile playerProfile = sqlDatabaseManager.newUser(player);
+//
+// // Validate values are starting from zero
+// for (PrimarySkillType primarySkillType : PrimarySkillType.values()) {
+// assertEquals(0, playerProfile.getSkillXpLevel(primarySkillType));
+// }
+//
+// // Change values
+// for (PrimarySkillType primarySkillType : PrimarySkillType.values()) {
+// playerProfile.setSkillXpLevel(primarySkillType, 1 + primarySkillType.ordinal());
+// }
+//
+// sqlDatabaseManager.saveUser(playerProfile);
+//
+// PlayerProfile retrievedUser = sqlDatabaseManager.loadPlayerProfile(player.getName());
+//
+// // Check that values got saved
+// for (PrimarySkillType primarySkillType : PrimarySkillType.values()) {
+// if (primarySkillType == PrimarySkillType.SALVAGE || primarySkillType == PrimarySkillType.SMELTING) {
+// // Child skills are not saved, but calculated
+// continue;
+// }
+//
+// assertEquals(1 + primarySkillType.ordinal(), retrievedUser.getSkillXpLevel(primarySkillType));
+// }
+// }
+//}
diff --git a/src/test/java/com/gmail/nossr50/locale/LocaleLoaderTest.java b/src/test/java/com/gmail/nossr50/locale/LocaleLoaderTest.java
new file mode 100644
index 000000000..d7db4bee3
--- /dev/null
+++ b/src/test/java/com/gmail/nossr50/locale/LocaleLoaderTest.java
@@ -0,0 +1,76 @@
+package com.gmail.nossr50.locale;
+
+import org.bukkit.ChatColor;
+import org.junit.jupiter.api.AfterEach;
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
+import org.junit.jupiter.params.ParameterizedTest;
+import org.junit.jupiter.params.provider.ValueSource;
+
+import java.util.List;
+
+import static org.assertj.core.api.Assertions.assertThat;
+
+class LocaleLoaderTest {
+
+ @BeforeEach
+ void setUp() {
+ }
+
+ @AfterEach
+ void tearDown() {
+ }
+
+ @ParameterizedTest
+ @ValueSource(strings = {"§cTest", "[[RED]]Test"})
+ void addColorsShouldAddColorRed(String testString) {
+ // When
+ final String result = LocaleLoader.addColors(testString);
+
+ // Then
+ assertThat(result).isEqualTo(ChatColor.RED + "Test");
+ }
+
+ // hex colors test
+ @Test
+ void translateHexColorCodesShouldAddRed() {
+ // Given
+ final String testString = "FF0000Test";
+
+ // When
+ final String result = LocaleLoader.translateHexColorCodes(testString);
+
+ // Then
+ final String expectedResult = "§x§F§F§0§0§0§0Test";
+ assertThat(result).isEqualTo(expectedResult);
+ }
+
+ @Test
+ void reverseTranslateHexColorCodesShouldRemoveRed() {
+ // Given
+ final String testString = "§x§F§F§0§0§0§0Test";
+
+ // When
+ final String result = LocaleLoader.reverseTranslateHexColorCodes(testString);
+
+ // Then
+ final String expectedResult = "FF0000Test";
+ assertThat(result).isEqualTo(expectedResult);
+ }
+
+ @ParameterizedTest
+ @ValueSource(strings = {"FF0000TeFFst", "FF0000Te[[RED]]st", "[[BLUE]]Te[[RED]]st", "§9Te§cst"})
+ void addColorsShouldAddRedAndBlue(String testString) {
+ // When
+ final String result = LocaleLoader.addColors(testString);
+
+ // TODO: Hacky, clean this up sometime in the future
+ // Then
+ // All legal representations of the same string
+ final List expectedResults = List.of("§x§F§F§0§0§0§0Te§x§0§0§0§0§F§Fst",
+ "§x§F§F§0§0§0§0Te§x§0§0§0§0§F§Fst",
+ "§x§F§F§0§0§0§0Te§cst",
+ "§9Te§cst");
+ assertThat(expectedResults).contains(result);
+ }
+}
\ No newline at end of file
diff --git a/src/test/java/com/gmail/nossr50/party/PartyManagerTest.java b/src/test/java/com/gmail/nossr50/party/PartyManagerTest.java
new file mode 100644
index 000000000..0d8f01ccb
--- /dev/null
+++ b/src/test/java/com/gmail/nossr50/party/PartyManagerTest.java
@@ -0,0 +1,97 @@
+package com.gmail.nossr50.party;
+
+import com.gmail.nossr50.MMOTestEnvironment;
+import com.gmail.nossr50.datatypes.player.McMMOPlayer;
+import com.gmail.nossr50.mcMMO;
+import org.bukkit.entity.Player;
+import org.junit.jupiter.api.AfterEach;
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
+import org.mockito.Mockito;
+
+import java.util.UUID;
+import java.util.logging.Logger;
+
+import static org.junit.jupiter.api.Assertions.assertThrows;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.when;
+
+class PartyManagerTest extends MMOTestEnvironment {
+ private static final Logger logger = Logger.getLogger(PartyManagerTest.class.getName());
+
+ @BeforeEach
+ public void setUp() {
+ mockBaseEnvironment(logger);
+
+ // currently unnecessary, but may be needed for future tests
+ Mockito.when(partyConfig.isPartyEnabled()).thenReturn(true);
+ }
+
+ @AfterEach
+ public void tearDown() {
+ cleanupBaseEnvironment();
+
+ // disable parties in config for other tests
+ Mockito.when(partyConfig.isPartyEnabled()).thenReturn(false);
+ }
+
+ @Test
+ public void createPartyWithoutPasswordShouldSucceed() {
+ // Given
+ PartyManager partyManager = new PartyManager(mcMMO.p);
+ String partyName = "TestParty";
+
+ Player player = mock(Player.class);
+ McMMOPlayer mmoPlayer = mock(McMMOPlayer.class);
+ when(mmoPlayer.getPlayer()).thenReturn(player);
+ when(player.getUniqueId()).thenReturn(new UUID(0, 0));
+
+ // When & Then
+ partyManager.createParty(mmoPlayer, partyName, null);
+ }
+
+ @Test
+ public void createPartyWithPasswordShouldSucceed() {
+ // Given
+ PartyManager partyManager = new PartyManager(mcMMO.p);
+ String partyName = "TestParty";
+ String partyPassword = "somePassword";
+
+ Player player = mock(Player.class);
+ McMMOPlayer mmoPlayer = mock(McMMOPlayer.class);
+ when(mmoPlayer.getPlayer()).thenReturn(player);
+ when(player.getUniqueId()).thenReturn(new UUID(0, 0));
+
+ // When & Then
+ partyManager.createParty(mmoPlayer, partyName, partyPassword);
+ }
+
+ @Test
+ public void createPartyWithoutNameShouldFail() {
+ // Given
+ PartyManager partyManager = new PartyManager(mcMMO.p);
+ String partyPassword = "somePassword";
+
+ Player player = mock(Player.class);
+ McMMOPlayer mmoPlayer = mock(McMMOPlayer.class);
+ when(mmoPlayer.getPlayer()).thenReturn(player);
+ when(player.getUniqueId()).thenReturn(new UUID(0, 0));
+
+ // When & Then
+ assertThrows(NullPointerException.class,
+ () -> partyManager.createParty(mmoPlayer, null, partyPassword));
+ }
+
+ @Test
+ public void createPartyWithoutPlayerShouldFail() {
+ // Given
+ PartyManager partyManager = new PartyManager(mcMMO.p);
+ String partyName = "TestParty";
+ String partyPassword = "somePassword";
+
+ // When & Then
+ assertThrows(NullPointerException.class,
+ () -> partyManager.createParty(null, partyName, partyPassword));
+ }
+
+}
\ No newline at end of file
diff --git a/src/test/java/com/gmail/nossr50/skills/excavation/ExcavationTest.java b/src/test/java/com/gmail/nossr50/skills/excavation/ExcavationTest.java
new file mode 100644
index 000000000..631696f0a
--- /dev/null
+++ b/src/test/java/com/gmail/nossr50/skills/excavation/ExcavationTest.java
@@ -0,0 +1,120 @@
+package com.gmail.nossr50.skills.excavation;
+
+import com.gmail.nossr50.MMOTestEnvironment;
+import com.gmail.nossr50.api.exceptions.InvalidSkillException;
+import com.gmail.nossr50.datatypes.skills.PrimarySkillType;
+import com.gmail.nossr50.datatypes.skills.SubSkillType;
+import com.gmail.nossr50.datatypes.treasure.ExcavationTreasure;
+import com.gmail.nossr50.util.skills.RankUtils;
+import org.bukkit.Location;
+import org.bukkit.Material;
+import org.bukkit.block.Block;
+import org.bukkit.block.BlockState;
+import org.bukkit.block.data.BlockData;
+import org.bukkit.entity.Player;
+import org.bukkit.inventory.ItemStack;
+import org.bukkit.inventory.PlayerInventory;
+import org.junit.jupiter.api.AfterEach;
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
+import org.mockito.Mockito;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.Mockito.*;
+
+class ExcavationTest extends MMOTestEnvironment {
+ private static final java.util.logging.Logger logger = java.util.logging.Logger.getLogger(ExcavationTest.class.getName());
+
+
+ @BeforeEach
+ void setUp() throws InvalidSkillException {
+ mockBaseEnvironment(logger);
+ when(rankConfig.getSubSkillUnlockLevel(SubSkillType.EXCAVATION_ARCHAEOLOGY, 1)).thenReturn(1);
+ when(rankConfig.getSubSkillUnlockLevel(SubSkillType.EXCAVATION_GIGA_DRILL_BREAKER, 1)).thenReturn(1);
+
+ // wire advanced config
+
+ when(RankUtils.getRankUnlockLevel(SubSkillType.EXCAVATION_ARCHAEOLOGY, 1)).thenReturn(1); // needed?
+ when(RankUtils.getRankUnlockLevel(SubSkillType.EXCAVATION_GIGA_DRILL_BREAKER, 1)).thenReturn(1); // needed?
+ when(RankUtils.hasReachedRank(eq(1), any(Player.class), eq(SubSkillType.EXCAVATION_ARCHAEOLOGY))).thenReturn(true);
+ when(RankUtils.hasReachedRank(eq(1), any(Player.class), eq(SubSkillType.EXCAVATION_GIGA_DRILL_BREAKER))).thenReturn(true);
+
+ // setup player and player related mocks after everything else
+ this.player = Mockito.mock(Player.class);
+ when(player.getUniqueId()).thenReturn(playerUUID);
+
+ // wire inventory
+ this.playerInventory = Mockito.mock(PlayerInventory.class);
+ this.itemInMainHand = new ItemStack(Material.DIAMOND_SHOVEL);
+ when(player.getInventory()).thenReturn(playerInventory);
+ when(playerInventory.getItemInMainHand()).thenReturn(itemInMainHand);
+
+ // Set up spy for Excavation Manager
+
+ }
+
+ @AfterEach
+ void tearDown() {
+ cleanupBaseEnvironment();
+ }
+
+ @Test
+ void excavationShouldHaveTreasureDrops() {
+ mmoPlayer.modifySkill(PrimarySkillType.EXCAVATION, 1000);
+
+ // Wire block
+ BlockState blockState = Mockito.mock(BlockState.class);
+ BlockData blockData = Mockito.mock(BlockData.class);
+ Block block = Mockito.mock(Block.class);
+ when(blockState.getBlockData()).thenReturn(blockData);
+ when(blockState.getType()).thenReturn(Material.SAND);
+ when(blockData.getMaterial()).thenReturn(Material.SAND);
+ when(blockState.getBlock()).thenReturn(block);
+ when(blockState.getBlock().getDrops(any())).thenReturn(null);
+
+ ExcavationManager excavationManager = Mockito.spy(new ExcavationManager(mmoPlayer));
+ doReturn(getGuaranteedTreasureDrops()).when(excavationManager).getTreasures(blockState);
+ excavationManager.excavationBlockCheck(blockState);
+
+ // verify ExcavationManager.processExcavationBonusesOnBlock was called
+ verify(excavationManager, atLeastOnce()).processExcavationBonusesOnBlock(any(BlockState.class), any(ExcavationTreasure.class), any(Location.class));
+ }
+
+ @Test
+ void excavationShouldNotDropTreasure() {
+ mmoPlayer.modifySkill(PrimarySkillType.EXCAVATION, 1000);
+
+ // Wire block
+ BlockState blockState = Mockito.mock(BlockState.class);
+ BlockData blockData = Mockito.mock(BlockData.class);
+ Block block = Mockito.mock(Block.class);
+ when(blockState.getBlockData()).thenReturn(blockData);
+ when(blockState.getType()).thenReturn(Material.SAND);
+ when(blockData.getMaterial()).thenReturn(Material.SAND);
+ when(blockState.getBlock()).thenReturn(block);
+ when(blockState.getBlock().getDrops(any())).thenReturn(null);
+
+ ExcavationManager excavationManager = Mockito.spy(new ExcavationManager(mmoPlayer));
+ doReturn(getImpossibleTreasureDrops()).when(excavationManager).getTreasures(blockState);
+ excavationManager.excavationBlockCheck(blockState);
+
+ // verify ExcavationManager.processExcavationBonusesOnBlock was called
+ verify(excavationManager, never()).processExcavationBonusesOnBlock(any(BlockState.class), any(ExcavationTreasure.class), any(Location.class));
+ }
+
+ private List getGuaranteedTreasureDrops() {
+ List treasures = new ArrayList<>();;
+ treasures.add(new ExcavationTreasure(new ItemStack(Material.CAKE), 1, 100, 1));
+ return treasures;
+ }
+
+ private List getImpossibleTreasureDrops() {
+ List treasures = new ArrayList<>();;
+ treasures.add(new ExcavationTreasure(new ItemStack(Material.CAKE), 1, 0, 1));
+ return treasures;
+ }
+}
diff --git a/src/test/java/com/gmail/nossr50/skills/tridents/TridentsTest.java b/src/test/java/com/gmail/nossr50/skills/tridents/TridentsTest.java
new file mode 100644
index 000000000..199c8e554
--- /dev/null
+++ b/src/test/java/com/gmail/nossr50/skills/tridents/TridentsTest.java
@@ -0,0 +1,39 @@
+package com.gmail.nossr50.skills.tridents;
+
+import com.gmail.nossr50.MMOTestEnvironment;
+import com.gmail.nossr50.api.exceptions.InvalidSkillException;
+import org.bukkit.Material;
+import org.bukkit.entity.Player;
+import org.bukkit.inventory.ItemStack;
+import org.bukkit.inventory.PlayerInventory;
+import org.junit.jupiter.api.AfterEach;
+import org.junit.jupiter.api.BeforeEach;
+import org.mockito.Mockito;
+
+class TridentsTest extends MMOTestEnvironment {
+ private static final java.util.logging.Logger logger = java.util.logging.Logger.getLogger(TridentsTest.class.getName());
+
+ TridentsManager tridentsManager;
+ ItemStack trident;
+ @BeforeEach
+ void setUp() throws InvalidSkillException {
+ mockBaseEnvironment(logger);
+
+ // setup player and player related mocks after everything else
+ this.player = Mockito.mock(Player.class);
+ Mockito.when(player.getUniqueId()).thenReturn(playerUUID);
+
+ // wire inventory
+ this.playerInventory = Mockito.mock(PlayerInventory.class);
+ this.trident = new ItemStack(Material.TRIDENT);
+ Mockito.when(playerInventory.getItemInMainHand()).thenReturn(trident);
+
+ // Set up spy for manager
+ tridentsManager = Mockito.spy(new TridentsManager(mmoPlayer));
+ }
+
+ @AfterEach
+ void tearDown() {
+ cleanupBaseEnvironment();
+ }
+}
diff --git a/src/test/java/com/gmail/nossr50/skills/woodcutting/WoodcuttingTest.java b/src/test/java/com/gmail/nossr50/skills/woodcutting/WoodcuttingTest.java
new file mode 100644
index 000000000..6e533fbe8
--- /dev/null
+++ b/src/test/java/com/gmail/nossr50/skills/woodcutting/WoodcuttingTest.java
@@ -0,0 +1,109 @@
+package com.gmail.nossr50.skills.woodcutting;
+
+import com.gmail.nossr50.MMOTestEnvironment;
+import com.gmail.nossr50.api.exceptions.InvalidSkillException;
+import com.gmail.nossr50.config.experience.ExperienceConfig;
+import com.gmail.nossr50.datatypes.skills.PrimarySkillType;
+import com.gmail.nossr50.datatypes.skills.SubSkillType;
+import com.gmail.nossr50.util.skills.RankUtils;
+import org.bukkit.Material;
+import org.bukkit.block.Block;
+import org.bukkit.block.BlockState;
+import org.bukkit.entity.Player;
+import org.bukkit.inventory.ItemStack;
+import org.bukkit.inventory.PlayerInventory;
+import org.junit.jupiter.api.AfterEach;
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
+import org.mockito.Mockito;
+
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.eq;
+
+class WoodcuttingTest extends MMOTestEnvironment {
+ private static final java.util.logging.Logger logger = java.util.logging.Logger.getLogger(WoodcuttingTest.class.getName());
+
+ WoodcuttingManager woodcuttingManager;
+ @BeforeEach
+ void setUp() throws InvalidSkillException {
+ mockBaseEnvironment(logger);
+ Mockito.when(rankConfig.getSubSkillUnlockLevel(SubSkillType.WOODCUTTING_HARVEST_LUMBER, 1)).thenReturn(1);
+
+ // wire advanced config
+ Mockito.when(advancedConfig.getMaximumProbability(SubSkillType.WOODCUTTING_HARVEST_LUMBER)).thenReturn(100D);
+ Mockito.when(advancedConfig.getMaximumProbability(SubSkillType.WOODCUTTING_CLEAN_CUTS)).thenReturn(10D);
+ Mockito.when(advancedConfig.getMaxBonusLevel(SubSkillType.WOODCUTTING_HARVEST_LUMBER)).thenReturn(1000);
+ Mockito.when(advancedConfig.getMaxBonusLevel(SubSkillType.WOODCUTTING_CLEAN_CUTS)).thenReturn(10000);
+
+ Mockito.when(RankUtils.getRankUnlockLevel(SubSkillType.WOODCUTTING_HARVEST_LUMBER, 1)).thenReturn(1); // needed?
+ Mockito.when(RankUtils.getRankUnlockLevel(SubSkillType.WOODCUTTING_CLEAN_CUTS, 1)).thenReturn(1000); // needed?
+ Mockito.when(RankUtils.hasReachedRank(eq(1), any(Player.class), eq(SubSkillType.WOODCUTTING_HARVEST_LUMBER))).thenReturn(true);
+ Mockito.when(RankUtils.hasReachedRank(eq(1), any(Player.class), eq(SubSkillType.WOODCUTTING_CLEAN_CUTS))).thenReturn(true);
+
+ // setup player and player related mocks after everything else
+ this.player = Mockito.mock(Player.class);
+ Mockito.when(player.getUniqueId()).thenReturn(playerUUID);
+
+ // wire inventory
+ this.playerInventory = Mockito.mock(PlayerInventory.class);
+ this.itemInMainHand = new ItemStack(Material.DIAMOND_AXE);
+ Mockito.when(player.getInventory()).thenReturn(playerInventory);
+ Mockito.when(playerInventory.getItemInMainHand()).thenReturn(itemInMainHand);
+
+ // Set up spy for WoodcuttingManager
+ woodcuttingManager = Mockito.spy(new WoodcuttingManager(mmoPlayer));
+ }
+
+ @AfterEach
+ void tearDown() {
+ cleanupBaseEnvironment();
+ }
+
+ @Test
+ void harvestLumberShouldDoubleDrop() {
+ mmoPlayer.modifySkill(PrimarySkillType.WOODCUTTING, 1000);
+
+ BlockState blockState = Mockito.mock(BlockState.class);
+ Block block = Mockito.mock(Block.class);
+ // wire block
+ Mockito.when(blockState.getBlock()).thenReturn(block);
+
+ Mockito.when(blockState.getBlock().getDrops(any())).thenReturn(null);
+ Mockito.when(blockState.getType()).thenReturn(Material.OAK_LOG);
+ woodcuttingManager.processBonusDropCheck(blockState);
+
+ // verify bonus drops were spawned
+ // TODO: using at least once since triple drops can also happen
+ // TODO: Change the test env to disallow triple drop in the future
+ Mockito.verify(woodcuttingManager, Mockito.atLeastOnce()).spawnHarvestLumberBonusDrops(blockState);
+ }
+
+ @Test
+ void harvestLumberShouldNotDoubleDrop() {
+ mmoPlayer.modifySkill(PrimarySkillType.WOODCUTTING, 0);
+
+ BlockState blockState = Mockito.mock(BlockState.class);
+ Block block = Mockito.mock(Block.class);
+ // wire block
+ Mockito.when(blockState.getBlock()).thenReturn(block);
+
+ Mockito.when(blockState.getBlock().getDrops(any())).thenReturn(null);
+ Mockito.when(blockState.getType()).thenReturn(Material.OAK_LOG);
+ woodcuttingManager.processBonusDropCheck(blockState);
+
+ // verify bonus drops were not spawned
+ Mockito.verify(woodcuttingManager, Mockito.times(0)).spawnHarvestLumberBonusDrops(blockState);
+ }
+
+ @Test
+ void testProcessWoodcuttingBlockXP() {
+ BlockState targetBlock = Mockito.mock(BlockState.class);
+ Mockito.when(targetBlock.getType()).thenReturn(Material.OAK_LOG);
+ // wire XP
+ Mockito.when(ExperienceConfig.getInstance().getXp(PrimarySkillType.WOODCUTTING, Material.OAK_LOG)).thenReturn(5);
+
+ // Verify XP increased by 5 when processing XP
+ woodcuttingManager.processWoodcuttingBlockXP(targetBlock);
+ Mockito.verify(mmoPlayer, Mockito.times(1)).beginXpGain(eq(PrimarySkillType.WOODCUTTING), eq(5F), any(), any());
+ }
+}
diff --git a/src/test/java/com/gmail/nossr50/util/random/ProbabilityTest.java b/src/test/java/com/gmail/nossr50/util/random/ProbabilityTest.java
new file mode 100644
index 000000000..e114313ba
--- /dev/null
+++ b/src/test/java/com/gmail/nossr50/util/random/ProbabilityTest.java
@@ -0,0 +1,105 @@
+package com.gmail.nossr50.util.random;
+
+import org.junit.jupiter.api.Test;
+import org.junit.jupiter.params.ParameterizedTest;
+import org.junit.jupiter.params.provider.Arguments;
+import org.junit.jupiter.params.provider.MethodSource;
+
+import java.util.stream.Stream;
+
+import static org.junit.jupiter.api.Assertions.*;
+
+class ProbabilityTest {
+
+ private static Stream provideProbabilitiesForWithinExpectations() {
+ return Stream.of(
+ // static probability, % of time for success
+ Arguments.of(new ProbabilityImpl(.05), 5),
+ Arguments.of(new ProbabilityImpl(.10), 10),
+ Arguments.of(new ProbabilityImpl(.15), 15),
+ Arguments.of(new ProbabilityImpl(.20), 20),
+ Arguments.of(new ProbabilityImpl(.25), 25),
+ Arguments.of(new ProbabilityImpl(.50), 50),
+ Arguments.of(new ProbabilityImpl(.75), 75),
+ Arguments.of(new ProbabilityImpl(.90), 90),
+ Arguments.of(new ProbabilityImpl(.999), 99.9),
+ Arguments.of(new ProbabilityImpl(0.0005), 0.05),
+ Arguments.of(new ProbabilityImpl(0.001), 0.1),
+ Arguments.of(new ProbabilityImpl(50.0), 100),
+ Arguments.of(new ProbabilityImpl(100.0), 100)
+ );
+ }
+
+ private static Stream provideOfPercentageProbabilitiesForWithinExpectations() {
+ return Stream.of(
+ // static probability, % of time for success
+ Arguments.of(Probability.ofPercent(5), 5),
+ Arguments.of(Probability.ofPercent(10), 10),
+ Arguments.of(Probability.ofPercent(15), 15),
+ Arguments.of(Probability.ofPercent(20), 20),
+ Arguments.of(Probability.ofPercent(25), 25),
+ Arguments.of(Probability.ofPercent(50), 50),
+ Arguments.of(Probability.ofPercent(75), 75),
+ Arguments.of(Probability.ofPercent(90), 90),
+ Arguments.of(Probability.ofPercent(99.9), 99.9),
+ Arguments.of(Probability.ofPercent(0.05), 0.05),
+ Arguments.of(Probability.ofPercent(0.1), 0.1),
+ Arguments.of(Probability.ofPercent(500), 100),
+ Arguments.of(Probability.ofPercent(1000), 100)
+ );
+ }
+ @Test
+ void testAlwaysWinConstructor() {
+ for (int i = 0; i < 100000; i++) {
+ assertTrue(new ProbabilityImpl(100).evaluate());
+ }
+ }
+
+ @Test
+ void testAlwaysLoseConstructor() {
+ for (int i = 0; i < 100000; i++) {
+ assertFalse(new ProbabilityImpl(0).evaluate());
+ }
+ }
+
+ @Test
+ void testAlwaysWinOfPercent() {
+ for (int i = 0; i < 100000; i++) {
+ assertTrue(Probability.ofPercent(100).evaluate());
+ }
+ }
+
+ @Test
+ void testAlwaysLoseOfPercent() {
+ for (int i = 0; i < 100000; i++) {
+ assertFalse(Probability.ofPercent(0).evaluate());
+ }
+ }
+
+ @ParameterizedTest
+ @MethodSource("provideProbabilitiesForWithinExpectations")
+ void testOddsExpectationsConstructor(Probability probability, double expectedWinPercent) {
+ assertExpectations(probability, expectedWinPercent);
+ }
+
+ @ParameterizedTest
+ @MethodSource("provideOfPercentageProbabilitiesForWithinExpectations")
+ void testOddsExpectationsOfPercent(Probability probability, double expectedWinPercent) {
+ assertExpectations(probability, expectedWinPercent);
+ }
+
+ private static void assertExpectations(Probability probability, double expectedWinPercent) {
+ double iterations = 2.0e7;
+ double winCount = 0;
+
+ for (int i = 0; i < iterations; i++) {
+ if(probability.evaluate()) {
+ winCount++;
+ }
+ }
+
+ double successPercent = (winCount / iterations) * 100;
+ System.out.println(successPercent + ", " + expectedWinPercent);
+ assertEquals(expectedWinPercent, successPercent, 0.05D);
+ }
+}
diff --git a/src/test/java/com/gmail/nossr50/util/random/ProbabilityTestUtils.java b/src/test/java/com/gmail/nossr50/util/random/ProbabilityTestUtils.java
new file mode 100644
index 000000000..e97119be6
--- /dev/null
+++ b/src/test/java/com/gmail/nossr50/util/random/ProbabilityTestUtils.java
@@ -0,0 +1,22 @@
+package com.gmail.nossr50.util.random;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+
+public class ProbabilityTestUtils {
+ public static void assertProbabilityExpectations(double expectedWinPercent, Probability probability) {
+ double iterations = 2.0e7; //20 million
+ double winCount = 0;
+ for (int i = 0; i < iterations; i++) {
+ if(probability.evaluate()) {
+ winCount++;
+ }
+ }
+
+ double successPercent = (winCount / iterations) * 100;
+ System.out.println("Wins: " + winCount);
+ System.out.println("Fails: " + (iterations - winCount));
+ System.out.println("Percentage succeeded: " + successPercent + ", Expected: " + expectedWinPercent);
+ assertEquals(expectedWinPercent, successPercent, 0.025D);
+ System.out.println("Variance is within tolerance levels!");
+ }
+}
diff --git a/src/test/java/com/gmail/nossr50/util/random/ProbabilityUtilTest.java b/src/test/java/com/gmail/nossr50/util/random/ProbabilityUtilTest.java
new file mode 100644
index 000000000..4994f8053
--- /dev/null
+++ b/src/test/java/com/gmail/nossr50/util/random/ProbabilityUtilTest.java
@@ -0,0 +1,75 @@
+package com.gmail.nossr50.util.random;
+
+import com.gmail.nossr50.MMOTestEnvironment;
+import com.gmail.nossr50.datatypes.skills.SubSkillType;
+import org.junit.jupiter.api.AfterEach;
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
+import org.junit.jupiter.params.ParameterizedTest;
+import org.junit.jupiter.params.provider.Arguments;
+import org.junit.jupiter.params.provider.MethodSource;
+
+import java.util.logging.Logger;
+import java.util.stream.Stream;
+
+import static com.gmail.nossr50.datatypes.skills.SubSkillType.*;
+import static com.gmail.nossr50.util.random.ProbabilityTestUtils.assertProbabilityExpectations;
+import static com.gmail.nossr50.util.random.ProbabilityUtil.calculateCurrentSkillProbability;
+import static java.util.logging.Logger.getLogger;
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.mockito.Mockito.when;
+
+class ProbabilityUtilTest extends MMOTestEnvironment {
+ private static final Logger logger = getLogger(ProbabilityUtilTest.class.getName());
+
+ final static double impactChance = 11D;
+ final static double greaterImpactChance = 0.007D;
+ final static double fastFoodChance = 45.5D;
+
+ @BeforeEach
+ public void setupMocks() {
+ mockBaseEnvironment(logger);
+ when(advancedConfig.getImpactChance()).thenReturn(impactChance);
+ when(advancedConfig.getGreaterImpactChance()).thenReturn(greaterImpactChance);
+ when(advancedConfig.getFastFoodChance()).thenReturn(fastFoodChance);
+ }
+
+ @AfterEach
+ public void tearDown() {
+ cleanupBaseEnvironment();
+ }
+
+ private static Stream staticChanceSkills() {
+ return Stream.of(
+ // static probability, % of time for success
+ Arguments.of(AXES_ARMOR_IMPACT, impactChance),
+ Arguments.of(AXES_GREATER_IMPACT, greaterImpactChance),
+ Arguments.of(TAMING_FAST_FOOD_SERVICE, fastFoodChance)
+ );
+ }
+
+ @ParameterizedTest
+ @MethodSource("staticChanceSkills")
+ void staticChanceSkillsShouldSucceedAsExpected(SubSkillType subSkillType, double expectedWinPercent)
+ throws InvalidStaticChance {
+ Probability staticRandomChance = ProbabilityUtil.getStaticRandomChance(subSkillType);
+ assertProbabilityExpectations(expectedWinPercent, staticRandomChance);
+ }
+
+ @Test
+ public void isSkillRNGSuccessfulShouldBehaveAsExpected() {
+ // Given
+ when(advancedConfig.getMaximumProbability(UNARMED_ARROW_DEFLECT)).thenReturn(20D);
+ when(advancedConfig.getMaxBonusLevel(UNARMED_ARROW_DEFLECT)).thenReturn(0);
+
+ final Probability probability = ProbabilityUtil.getSkillProbability(UNARMED_ARROW_DEFLECT, player);
+ assertEquals(0.2D, probability.getValue());
+ assertProbabilityExpectations(20, probability);
+ }
+
+ @Test
+ public void calculateCurrentSkillProbabilityShouldBeTwenty() {
+ final Probability probability = calculateCurrentSkillProbability(1000, 0, 20, 1000);
+ assertEquals(0.2D, probability.getValue());
+ }
+}
diff --git a/src/test/java/com/gmail/nossr50/util/random/RandomChanceTest.java b/src/test/java/com/gmail/nossr50/util/random/RandomChanceTest.java
deleted file mode 100644
index f28e7e842..000000000
--- a/src/test/java/com/gmail/nossr50/util/random/RandomChanceTest.java
+++ /dev/null
@@ -1,116 +0,0 @@
-//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);
-// }
-//}
diff --git a/src/test/java/com/gmail/nossr50/util/skills/SkillToolsTest.java b/src/test/java/com/gmail/nossr50/util/skills/SkillToolsTest.java
deleted file mode 100644
index 4a295d5d3..000000000
--- a/src/test/java/com/gmail/nossr50/util/skills/SkillToolsTest.java
+++ /dev/null
@@ -1,16 +0,0 @@
-//package com.gmail.nossr50.util.skills;
-//
-//import com.gmail.nossr50.datatypes.skills.PrimarySkillType;
-//import com.google.common.collect.ImmutableList;
-//import org.junit.Before;
-//import org.junit.Test;
-//import org.junit.runner.RunWith;
-//import org.powermock.core.classloader.annotations.PrepareForTest;
-//import org.powermock.core.classloader.annotations.SuppressStaticInitializationFor;
-//import org.powermock.modules.junit4.PowerMockRunner;
-//
-//@RunWith(PowerMockRunner.class)
-//@PrepareForTest(SkillTools.class)
-//public class SkillToolsTest {
-//
-//}
\ No newline at end of file
diff --git a/src/test/java/com/gmail/nossr50/util/text/TextUtilsTest.java b/src/test/java/com/gmail/nossr50/util/text/TextUtilsTest.java
index cefbe7010..c58157179 100644
--- a/src/test/java/com/gmail/nossr50/util/text/TextUtilsTest.java
+++ b/src/test/java/com/gmail/nossr50/util/text/TextUtilsTest.java
@@ -7,7 +7,7 @@ import org.junit.jupiter.api.Test;
/**
* This Unit Test checks if Adventure was set up correctly and works as expected.
- * Normally we can rely on this to be the case. However sometimes our dependencies
+ * Normally, we can rely on this to be the case. However sometimes our dependencies
* lack so far behind that things stop working correctly.
* This test ensures that basic functionality is guaranteed to work as we would expect.
*
diff --git a/src/test/resources/healthydb.users b/src/test/resources/healthydb.users
index 7ce5ccbad..79a2c7e70 100644
--- a/src/test/resources/healthydb.users
+++ b/src/test/resources/healthydb.users
@@ -1,3 +1,3 @@
-nossr50:1:IGNORED:IGNORED:10:2:20:3:4:5:6:7:8:9:10:30:40:50:60:70:80:90:100:IGNORED:11:110:111:222:333:444:555:666:777:IGNORED:12:120:888:IGNORED:HEARTS:13:130:588fe472-1c82-4c4e-9aa1-7eefccb277e3:1111:999:2020:
-mrfloris:2420:::0:2452:0:1983:1937:1790:3042:1138:3102:2408:3411:0:0:0:0:0:0:0:0::642:0:1617583171:0:1617165043:0:1617583004:1617563189:1616785408::2184:0:0:1617852413:HEARTS:415:0:631e3896-da2a-4077-974b-d047859d76bc:5:1600906906:3030:
-powerless:0:::0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0::0:0:0:0:0:0:0:0:0::0:0:0:1337:HEARTS:0:0:e0d07db8-f7e8-43c7-9ded-864dfc6f3b7c:5:1600906906:4040:
\ No newline at end of file
+nossr50:1:IGNORED:IGNORED:10:2:20:3:4:5:6:7:8:9:10:30:40:50:60:70:80:90:100:IGNORED:11:110:111:222:333:444:555:666:777:IGNORED:12:120:888:IGNORED:HEARTS:13:130:588fe472-1c82-4c4e-9aa1-7eefccb277e3:1111:999:2020:140:14:150:15:1111:2222:3333:
+mrfloris:2420:::0:2452:0:1983:1937:1790:3042:1138:3102:2408:3411:0:0:0:0:0:0:0:0::642:0:1617583171:0:1617165043:0:1617583004:1617563189:1616785408::2184:0:0:1617852413:HEARTS:415:0:631e3896-da2a-4077-974b-d047859d76bc:5:1600906906:3030:0:0:0:0:0:0:0:
+powerless:0:::0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0::0:0:0:0:0:0:0:0:0::0:0:0:1337:HEARTS:0:0:e0d07db8-f7e8-43c7-9ded-864dfc6f3b7c:5:1600906906:4040:0:0:0:0:0:0:0:
\ No newline at end of file
diff --git a/src/test/resources/olderdb.users b/src/test/resources/olderdb.users
new file mode 100644
index 000000000..7ce5ccbad
--- /dev/null
+++ b/src/test/resources/olderdb.users
@@ -0,0 +1,3 @@
+nossr50:1:IGNORED:IGNORED:10:2:20:3:4:5:6:7:8:9:10:30:40:50:60:70:80:90:100:IGNORED:11:110:111:222:333:444:555:666:777:IGNORED:12:120:888:IGNORED:HEARTS:13:130:588fe472-1c82-4c4e-9aa1-7eefccb277e3:1111:999:2020:
+mrfloris:2420:::0:2452:0:1983:1937:1790:3042:1138:3102:2408:3411:0:0:0:0:0:0:0:0::642:0:1617583171:0:1617165043:0:1617583004:1617563189:1616785408::2184:0:0:1617852413:HEARTS:415:0:631e3896-da2a-4077-974b-d047859d76bc:5:1600906906:3030:
+powerless:0:::0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0::0:0:0:0:0:0:0:0:0::0:0:0:1337:HEARTS:0:0:e0d07db8-f7e8-43c7-9ded-864dfc6f3b7c:5:1600906906:4040:
\ No newline at end of file