Просмотр исходного кода

new mmoinfo command and JSON click events

nossr50 6 лет назад
Родитель
Сommit
d5a4103858

+ 4 - 1
Changelog.txt

@@ -13,12 +13,13 @@ Version 2.1.0
  + You can now disable specific skills in coreskills.yml without the need for permissions
  + You can now disable specific skills in coreskills.yml without the need for permissions
  + Added links to mcMMO related websites to various commands
  + Added links to mcMMO related websites to various commands
  + Certain elements of mcMMO's UI have been restyled
  + Certain elements of mcMMO's UI have been restyled
- + added the subtext "Overhaul Era" to various locations until 3.0.0 comes out
+ + Added the tagline "Overhaul Era" to various locations until 3.0.0 comes out
  + (Config) New config file added coreskills.yml
  + (Config) New config file added coreskills.yml
  + (Config) Added rank settings for the new Woodcutting skill
  + (Config) Added rank settings for the new Woodcutting skill
  + (Config) Added configurable parameters for the new Tree Feller
  + (Config) Added configurable parameters for the new Tree Feller
  + (Config) Added classic toggle for Tree Feller
  + (Config) Added classic toggle for Tree Feller
  + (Chat) Added ability for admins to spy on party chat (off unless toggled on)
  + (Chat) Added ability for admins to spy on party chat (off unless toggled on)
+ + (Commands) Added new info command /mmoinfo or /mcinfo
  + (Commands) Added toggle command /mcchatspy
  + (Commands) Added toggle command /mcchatspy
  + (API) Added many missing SubSkills to SubSkillType class
  + (API) Added many missing SubSkills to SubSkillType class
  + (Permissions) Added permission node mcmmo.commands.mcchatspy & mcmmo.commands.mcchatspy.others
  + (Permissions) Added permission node mcmmo.commands.mcchatspy & mcmmo.commands.mcchatspy.others
@@ -48,11 +49,13 @@ Version 2.1.0
  ! (Config) Axes.CriticalHit in advanced.yml is now Axes.CriticalStrikes
  ! (Config) Axes.CriticalHit in advanced.yml is now Axes.CriticalStrikes
  ! (Config) Archery's Skill Shot now uses RankDamageMultiplier for its damage bonus calculations
  ! (Config) Archery's Skill Shot now uses RankDamageMultiplier for its damage bonus calculations
  ! (Config) Axe's Axe mastery now uses RankDamageMultiplier for its damage bonus calculations
  ! (Config) Axe's Axe mastery now uses RankDamageMultiplier for its damage bonus calculations
+ + (Permissions) Added mcmmo.commands.mmoinfo for the new mmoinfo/mcinfo command
  ! (Permissions) Replaced the old Double Drop permission node for woodcutting with a new Harvest Lumber permission node
  ! (Permissions) Replaced the old Double Drop permission node for woodcutting with a new Harvest Lumber permission node
  ! (Permissions) Fast Food Service permission node renamed to mcmmo.ability.taming.fastfoodservice
  ! (Permissions) Fast Food Service permission node renamed to mcmmo.ability.taming.fastfoodservice
  ! (Permissions) Counter Attack permission node renamed to mcmmo.ability.swords.counterattack
  ! (Permissions) Counter Attack permission node renamed to mcmmo.ability.swords.counterattack
  ! (Permissions) Arrow Deflect permission node renamed to mcmmo.ability.unarmed.arrowdeflect
  ! (Permissions) Arrow Deflect permission node renamed to mcmmo.ability.unarmed.arrowdeflect
  ! (Permissions) Iron Arm Style permission node renamed to mcmmo.ability.unarmed.ironarmstyle
  ! (Permissions) Iron Arm Style permission node renamed to mcmmo.ability.unarmed.ironarmstyle
+ ! (Locale) The descriptions of a few skills have changed
  ! (Locale) Removed redundant information from some skill names and descriptions en_US (other locales will need to be updated)
  ! (Locale) Removed redundant information from some skill names and descriptions en_US (other locales will need to be updated)
  ! (Locale) SubSkill locale keys are now located at {ParentSkill}.SubSkill.SubSkillName
  ! (Locale) SubSkill locale keys are now located at {ParentSkill}.SubSkill.SubSkillName
  ! (Locale) Super Abilities no longer have (ABILITY) in their Skill.Effect strings
  ! (Locale) Super Abilities no longer have (ABILITY) in their Skill.Effect strings

+ 80 - 0
src/main/java/com/gmail/nossr50/commands/skills/MmoInfo.java

@@ -0,0 +1,80 @@
+package com.gmail.nossr50.commands.skills;
+
+import com.gmail.nossr50.datatypes.skills.PrimarySkill;
+import com.gmail.nossr50.datatypes.skills.subskills.AbstractSubSkill;
+import com.gmail.nossr50.listeners.InteractionManager;
+import com.gmail.nossr50.locale.LocaleLoader;
+import com.gmail.nossr50.util.Permissions;
+import com.gmail.nossr50.util.TextComponentFactory;
+import com.google.common.collect.ImmutableList;
+import org.bukkit.command.Command;
+import org.bukkit.command.CommandSender;
+import org.bukkit.command.TabExecutor;
+import org.bukkit.entity.Player;
+import org.bukkit.util.StringUtil;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * This is the command that retrieves data about skills from in-game sources
+ */
+public class MmoInfo implements TabExecutor {
+
+    @Override
+    public boolean onCommand(CommandSender commandSender, Command command, String s, String[] args) {
+        if(commandSender instanceof Player)
+        {
+            Player player = (Player) commandSender;
+            if(Permissions.mmoinfo(player))
+            {
+                if(args == null || args[0] == null)
+                    return false;
+
+                //Real skill
+                if(InteractionManager.getAbstractByName(args[0]) != null || PrimarySkill.SUBSKILL_NAMES.contains(args[0]))
+                {
+                    displayInfo(player, args[0]);
+                    return true;
+                }
+
+                //Not a real skill
+                player.sendMessage(LocaleLoader.getString("Commands.MmoInfo.NoMatch"));
+            }
+        }
+        return false;
+    }
+
+    @Override
+    public List<String> onTabComplete(CommandSender sender, Command command, String alias, String[] args) {
+        switch (args.length) {
+            case 1:
+                return StringUtil.copyPartialMatches(args[0], PrimarySkill.SUBSKILL_NAMES, new ArrayList<String>(PrimarySkill.SUBSKILL_NAMES.size()));
+            default:
+                return ImmutableList.of();
+        }
+    }
+
+    private void displayInfo(Player player, String subSkillName)
+    {
+        System.out.println("[mcMMO] Debug: Grabbing info for skill "+subSkillName);
+
+        //Check to see if the skill exists in the new system
+        AbstractSubSkill abstractSubSkill = InteractionManager.getAbstractByName(subSkillName);
+        if(abstractSubSkill != null)
+        {
+            /* New System Skills are programmable */
+            abstractSubSkill.printInfo(player);
+            //TextComponentFactory.sendPlayerUrlHeader(player);
+        } else {
+            /*
+             * Skill is only in the old system
+             */
+            player.sendMessage(LocaleLoader.getString("Commands.MmoInfo.Header"));
+            player.sendMessage(LocaleLoader.getString("Commands.MmoInfo.SubSkillHeader", subSkillName));
+            player.sendMessage(LocaleLoader.getString("Commands.MmoInfo.DetailsHeader"));
+            player.sendMessage(LocaleLoader.getString("Commands.MmoInfo.OldSkill"));
+            //TextComponentFactory.sendPlayerUrlHeader(player);
+        }
+    }
+}

+ 7 - 0
src/main/java/com/gmail/nossr50/datatypes/skills/PrimarySkill.java

@@ -59,6 +59,7 @@ public enum PrimarySkill {
     private List<SubSkillType> subSkillTypes;
     private List<SubSkillType> subSkillTypes;
 
 
     public static final List<String> SKILL_NAMES;
     public static final List<String> SKILL_NAMES;
+    public static final List<String> SUBSKILL_NAMES;
 
 
     public static final List<PrimarySkill> CHILD_SKILLS;
     public static final List<PrimarySkill> CHILD_SKILLS;
     public static final List<PrimarySkill> NON_CHILD_SKILLS;
     public static final List<PrimarySkill> NON_CHILD_SKILLS;
@@ -71,6 +72,7 @@ public enum PrimarySkill {
         List<PrimarySkill> childSkills = new ArrayList<PrimarySkill>();
         List<PrimarySkill> childSkills = new ArrayList<PrimarySkill>();
         List<PrimarySkill> nonChildSkills = new ArrayList<PrimarySkill>();
         List<PrimarySkill> nonChildSkills = new ArrayList<PrimarySkill>();
         ArrayList<String> names = new ArrayList<String>();
         ArrayList<String> names = new ArrayList<String>();
+        ArrayList<String> subSkillNames = new ArrayList<>();
 
 
         for (PrimarySkill skill : values()) {
         for (PrimarySkill skill : values()) {
             if (skill.isChildSkill()) {
             if (skill.isChildSkill()) {
@@ -80,11 +82,16 @@ public enum PrimarySkill {
                 nonChildSkills.add(skill);
                 nonChildSkills.add(skill);
             }
             }
 
 
+            for(SubSkillType subSkillType : skill.subSkillTypes)
+            {
+                subSkillNames.add(subSkillType.getNiceNameNoSpaces(subSkillType));
+            }
             names.add(skill.getName());
             names.add(skill.getName());
         }
         }
 
 
         Collections.sort(names);
         Collections.sort(names);
         SKILL_NAMES = ImmutableList.copyOf(names);
         SKILL_NAMES = ImmutableList.copyOf(names);
+        SUBSKILL_NAMES = ImmutableList.copyOf(subSkillNames);
 
 
         CHILD_SKILLS = ImmutableList.copyOf(childSkills);
         CHILD_SKILLS = ImmutableList.copyOf(childSkills);
         NON_CHILD_SKILLS = ImmutableList.copyOf(nonChildSkills);
         NON_CHILD_SKILLS = ImmutableList.copyOf(nonChildSkills);

+ 10 - 0
src/main/java/com/gmail/nossr50/datatypes/skills/SubSkillType.java

@@ -195,6 +195,16 @@ public enum SubSkillType {
         return LocaleLoader.getString(StringUtils.getCapitalized(getParentSkill().toString())+".SkillName");
         return LocaleLoader.getString(StringUtils.getCapitalized(getParentSkill().toString())+".SkillName");
     }
     }
 
 
+    /**
+     * Gets the "nice" name of the subskill without spaces
+     * @param subSkillType target subskill
+     * @return the "nice" name without spaces
+     */
+    public String getNiceNameNoSpaces(SubSkillType subSkillType)
+    {
+        return getConfigName(subSkillType.toString());
+    }
+
     /**
     /**
      * This finds the substring index for our SubSkillType's name after its parent name prefix
      * This finds the substring index for our SubSkillType's name after its parent name prefix
      * @param subSkillName The name to process
      * @param subSkillName The name to process

+ 18 - 0
src/main/java/com/gmail/nossr50/datatypes/skills/subskills/AbstractSubSkill.java

@@ -6,6 +6,8 @@ import com.gmail.nossr50.datatypes.skills.subskills.interfaces.Rank;
 import com.gmail.nossr50.datatypes.skills.subskills.interfaces.SubSkill;
 import com.gmail.nossr50.datatypes.skills.subskills.interfaces.SubSkill;
 import com.gmail.nossr50.datatypes.skills.subskills.interfaces.SubSkillProperties;
 import com.gmail.nossr50.datatypes.skills.subskills.interfaces.SubSkillProperties;
 import com.gmail.nossr50.locale.LocaleLoader;
 import com.gmail.nossr50.locale.LocaleLoader;
+import com.gmail.nossr50.util.TextComponentFactory;
+import org.bukkit.entity.Player;
 
 
 public abstract class AbstractSubSkill implements SubSkill, Interaction, Rank, SubSkillProperties {
 public abstract class AbstractSubSkill implements SubSkill, Interaction, Rank, SubSkillProperties {
     /*
     /*
@@ -40,4 +42,20 @@ public abstract class AbstractSubSkill implements SubSkill, Interaction, Rank, S
         //TODO: This might be troublesome...
         //TODO: This might be troublesome...
         return CoreSkillsConfig.getInstance().isSkillEnabled(this);
         return CoreSkillsConfig.getInstance().isSkillEnabled(this);
     }
     }
+
+    /**
+     * Prints detailed info about this subskill to the player
+     *
+     * @param player the target player
+     */
+    @Override
+    public void printInfo(Player player) {
+        /* DEFAULT SETTINGS PRINT THE BARE MINIMUM */
+
+        //TextComponentFactory.sendPlayerUrlHeader(player);
+        player.sendMessage(LocaleLoader.getString("Commands.MmoInfo.Header"));
+        player.sendMessage(LocaleLoader.getString("Commands.MmoInfo.SubSkillHeader", getConfigKeyName()));
+        player.sendMessage(LocaleLoader.getString("Commands.MmoInfo.DetailsHeader"));
+
+    }
 }
 }

+ 79 - 2
src/main/java/com/gmail/nossr50/datatypes/skills/subskills/acrobatics/Roll.java

@@ -29,7 +29,6 @@ import org.bukkit.event.EventPriority;
 import org.bukkit.event.entity.EntityDamageEvent;
 import org.bukkit.event.entity.EntityDamageEvent;
 import org.bukkit.inventory.ItemStack;
 import org.bukkit.inventory.ItemStack;
 
 
-
 public class Roll extends AcrobaticsSubSkill implements RandomChance {
 public class Roll extends AcrobaticsSubSkill implements RandomChance {
     private int fallTries = 0;
     private int fallTries = 0;
     protected Location lastFallLocation;
     protected Location lastFallLocation;
@@ -137,7 +136,6 @@ public class Roll extends AcrobaticsSubSkill implements RandomChance {
          *   Append the messages
          *   Append the messages
          */
          */
 
 
-
         /*componentBuilder.append(LocaleLoader.getString("Effects.Template", LocaleLoader.getString("Acrobatics.Effect.2"), LocaleLoader.getString("Acrobatics.Effect.3")));
         /*componentBuilder.append(LocaleLoader.getString("Effects.Template", LocaleLoader.getString("Acrobatics.Effect.2"), LocaleLoader.getString("Acrobatics.Effect.3")));
         componentBuilder.append("\n");*/
         componentBuilder.append("\n");*/
 
 
@@ -319,4 +317,83 @@ public class Roll extends AcrobaticsSubSkill implements RandomChance {
     public int getNumRanks() {
     public int getNumRanks() {
         return 0;
         return 0;
     }
     }
+
+    /**
+     * Prints detailed info about this subskill to the player
+     *
+     * @param player the target player
+     */
+    @Override
+    public void printInfo(Player player) {
+        //Header
+        super.printInfo(player);
+
+        //Start the description string.
+        //player.sendMessage(getDescription());
+        //Player stats
+        player.sendMessage(LocaleLoader.getString("Commands.MmoInfo.Stats",
+                            LocaleLoader.getString("Acrobatics.SubSkill.Roll.Stats", getStats(player)[0], getStats(player)[1])));
+
+        //Mechanics
+        player.sendMessage(LocaleLoader.getString("Commands.MmoInfo.Mechanics"));
+        player.sendMessage(getMechanics());
+    }
+
+    /**
+     * Returns a collection of strings about how a skill works
+     *
+     * @return
+     */
+    @Override
+    public String getMechanics() {
+        //Vars passed to locale
+        //0 = chance to roll at half max level
+        //1 = chance to roll with grace at half max level
+        //2 = level where maximum bonus is reached
+        //3 = additive chance to succeed per level
+        /*
+        Roll:
+            # ChanceMax: Maximum chance of rolling when on <MaxBonusLevel> or higher
+            # MaxBonusLevel: On this level or higher, the roll chance will not go higher than <ChanceMax>
+            # DamageThreshold: The max damage a player can negate with a roll
+            ChanceMax: 100.0
+            MaxBonusLevel: 100
+            DamageThreshold: 7.0
+         */
+        double rollChanceHalfMax, graceChanceHalfMax, maxBonusLevel, curve, damageThreshold, chancePerLevel;
+
+        curve = AdvancedConfig.getInstance().getMaxChance(this);
+        maxBonusLevel = (double) AdvancedConfig.getInstance().getMaxBonusLevel(this);
+
+        //Chance
+        rollChanceHalfMax   = 100 * SkillUtils.getChanceOfSuccess(maxBonusLevel / 2, maxBonusLevel, curve);
+        graceChanceHalfMax  = 100 * SkillUtils.getChanceOfSuccess(maxBonusLevel / 2, maxBonusLevel, curve / 2);
+        damageThreshold     = AdvancedConfig.getInstance().getRollDamageThreshold();
+
+        chancePerLevel = (1/curve) * maxBonusLevel;
+
+
+        return LocaleLoader.getString("Acrobatics.SubSkill.Roll.Mechanics", rollChanceHalfMax, graceChanceHalfMax, maxBonusLevel, chancePerLevel, damageThreshold);
+    }
+
+    /**
+     * Get an array of various stats for a player
+     *
+     * @param player target player
+     * @return stat array for target player for this skill
+     */
+    @Override
+    public Double[] getStats(Player player)
+    {
+        double curve, maxBonusLevel, playerChanceRoll, playerChanceGrace;
+
+        curve = AdvancedConfig.getInstance().getMaxChance(this);
+        maxBonusLevel = (double) AdvancedConfig.getInstance().getMaxBonusLevel(this);
+
+        playerChanceRoll        = 100 * SkillUtils.getChanceOfSuccess(UserManager.getPlayer(player).getSkillLevel(getPrimarySkill()), maxBonusLevel, curve);
+        playerChanceGrace       = 100 * SkillUtils.getChanceOfSuccess(UserManager.getPlayer(player).getSkillLevel(getPrimarySkill()), maxBonusLevel, curve / 2);
+
+        Double[] stats = { playerChanceRoll, playerChanceGrace};
+        return stats;
+    }
 }
 }

+ 21 - 0
src/main/java/com/gmail/nossr50/datatypes/skills/subskills/interfaces/SubSkill.java

@@ -4,6 +4,8 @@ import com.gmail.nossr50.datatypes.skills.interfaces.Skill;
 import net.md_5.bungee.api.chat.ComponentBuilder;
 import net.md_5.bungee.api.chat.ComponentBuilder;
 import org.bukkit.entity.Player;
 import org.bukkit.entity.Player;
 
 
+import java.util.ArrayList;
+
 public interface SubSkill extends Skill {
 public interface SubSkill extends Skill {
     /**
     /**
      * Grabs the permission node for this skill
      * Grabs the permission node for this skill
@@ -11,6 +13,19 @@ public interface SubSkill extends Skill {
      */
      */
     String getPermissionNode();
     String getPermissionNode();
 
 
+    /**
+     * Returns a collection of strings about how a skill works
+     * @return
+     */
+    String getMechanics();
+
+    /**
+     * Get an array of various stats for a player
+     * @param player target player
+     * @return stat array for target player for this skill
+     */
+    Double[] getStats(Player player);
+
     /**
     /**
      * Checks if a player has permission to use this skill
      * Checks if a player has permission to use this skill
      * @param player target player
      * @param player target player
@@ -55,4 +70,10 @@ public interface SubSkill extends Skill {
      * @return true if enabled
      * @return true if enabled
      */
      */
     boolean isEnabled();
     boolean isEnabled();
+
+    /**
+     * Prints detailed info about this subskill to the player
+     * @param player the target player
+     */
+    void printInfo(Player player);
 }
 }

+ 22 - 3
src/main/java/com/gmail/nossr50/listeners/InteractionManager.java

@@ -19,6 +19,7 @@ import java.util.HashMap;
 
 
 public class InteractionManager {
 public class InteractionManager {
     private static HashMap<InteractType, ArrayList<Interaction>> interactRegister;
     private static HashMap<InteractType, ArrayList<Interaction>> interactRegister;
+    private static HashMap<String, AbstractSubSkill> subSkillNameMap; //Used for mmoinfo optimization
     private static ArrayList<AbstractSubSkill> subSkillList;
     private static ArrayList<AbstractSubSkill> subSkillList;
 
 
     /**
     /**
@@ -27,13 +28,16 @@ public class InteractionManager {
      */
      */
     public static void registerSubSkill(AbstractSubSkill abstractSubSkill)
     public static void registerSubSkill(AbstractSubSkill abstractSubSkill)
     {
     {
-        //Init map
+        /* INIT MAPS */
         if(interactRegister == null)
         if(interactRegister == null)
             interactRegister = new HashMap<>();
             interactRegister = new HashMap<>();
 
 
         if(subSkillList == null)
         if(subSkillList == null)
             subSkillList = new ArrayList<>();
             subSkillList = new ArrayList<>();
 
 
+        if(subSkillNameMap == null)
+            subSkillNameMap = new HashMap<>();
+
         //Store a unique copy of each subskill
         //Store a unique copy of each subskill
         if(!subSkillList.contains(abstractSubSkill))
         if(!subSkillList.contains(abstractSubSkill))
             subSkillList.add(abstractSubSkill);
             subSkillList.add(abstractSubSkill);
@@ -47,12 +51,27 @@ public class InteractionManager {
 
 
         //Register skill
         //Register skill
         arrayRef.add(abstractSubSkill);
         arrayRef.add(abstractSubSkill);
-        //TEST
-        //interactRegister.put(abstractSubSkill.getInteractType(), arrayRef);
+
+        String lowerCaseName = abstractSubSkill.getConfigKeyName().toLowerCase();
+
+        //Register in name map
+        if(subSkillNameMap.get(lowerCaseName) == null)
+            subSkillNameMap.put(lowerCaseName, abstractSubSkill);
 
 
         System.out.println("[mcMMO] registered subskill: "+ abstractSubSkill.getConfigKeyName());
         System.out.println("[mcMMO] registered subskill: "+ abstractSubSkill.getConfigKeyName());
     }
     }
 
 
+    /**
+     * Grabs the registered abstract skill by its name
+     * Is not case sensitive
+     * @param name name of subskill, not case sensitive
+     * @return null if the subskill is not registered
+     */
+    public static AbstractSubSkill getAbstractByName(String name)
+    {
+        return subSkillNameMap.get(name.toLowerCase());
+    }
+
     /**
     /**
      * Processes the associated Interactions for this event
      * Processes the associated Interactions for this event
      * @param event target event
      * @param event target event

+ 1 - 0
src/main/java/com/gmail/nossr50/util/Permissions.java

@@ -43,6 +43,7 @@ public final class Permissions {
      * COMMANDS
      * COMMANDS
      */
      */
 
 
+    public static boolean mmoinfo(Permissible permissible) { return permissible.hasPermission("mcmmo.commands.mmoinfo"); }
     public static boolean addlevels(Permissible permissible) { return permissible.hasPermission("mcmmo.commands.addlevels"); }
     public static boolean addlevels(Permissible permissible) { return permissible.hasPermission("mcmmo.commands.addlevels"); }
     public static boolean addlevelsOthers(Permissible permissible) { return permissible.hasPermission("mcmmo.commands.addlevels.others"); }
     public static boolean addlevelsOthers(Permissible permissible) { return permissible.hasPermission("mcmmo.commands.addlevels.others"); }
 
 

+ 2 - 1
src/main/java/com/gmail/nossr50/util/TextComponentFactory.java

@@ -252,6 +252,7 @@ public class TextComponentFactory {
 
 
             //Insertion
             //Insertion
             textComponent.setInsertion(skillName);
             textComponent.setInsertion(skillName);
+            textComponent.setClickEvent(new ClickEvent(ClickEvent.Action.RUN_COMMAND, "/mmoinfo "+subSkillType.getNiceNameNoSpaces(subSkillType)));
 
 
             subSkillTextComponents.put(key, textComponent);
             subSkillTextComponents.put(key, textComponent);
             return subSkillTextComponents.get(key);
             return subSkillTextComponents.get(key);
@@ -285,6 +286,7 @@ public class TextComponentFactory {
 
 
             //Insertion
             //Insertion
             textComponent.setInsertion(skillName);
             textComponent.setInsertion(skillName);
+            textComponent.setClickEvent(new ClickEvent(ClickEvent.Action.RUN_COMMAND, "/mmoinfo "+abstractSubSkill.getConfigKeyName()));
 
 
             subSkillTextComponents.put(key, textComponent);
             subSkillTextComponents.put(key, textComponent);
             return subSkillTextComponents.get(key);
             return subSkillTextComponents.get(key);
@@ -295,7 +297,6 @@ public class TextComponentFactory {
 
 
     private static BaseComponent[] getBaseComponent(Player player, AbstractSubSkill abstractSubSkill)
     private static BaseComponent[] getBaseComponent(Player player, AbstractSubSkill abstractSubSkill)
     {
     {
-
         int curRank = RankUtils.getRank(player, abstractSubSkill);
         int curRank = RankUtils.getRank(player, abstractSubSkill);
         String key = abstractSubSkill.getConfigKeyName();
         String key = abstractSubSkill.getConfigKeyName();
 
 

+ 11 - 15
src/main/java/com/gmail/nossr50/util/commands/CommandRegistrationManager.java

@@ -4,6 +4,7 @@ import java.util.ArrayList;
 import java.util.List;
 import java.util.List;
 
 
 import com.gmail.nossr50.commands.chat.McChatSpy;
 import com.gmail.nossr50.commands.chat.McChatSpy;
+import com.gmail.nossr50.commands.skills.*;
 import org.bukkit.command.PluginCommand;
 import org.bukkit.command.PluginCommand;
 
 
 import com.gmail.nossr50.mcMMO;
 import com.gmail.nossr50.mcMMO;
@@ -37,21 +38,6 @@ import com.gmail.nossr50.commands.player.MccooldownCommand;
 import com.gmail.nossr50.commands.player.McrankCommand;
 import com.gmail.nossr50.commands.player.McrankCommand;
 import com.gmail.nossr50.commands.player.McstatsCommand;
 import com.gmail.nossr50.commands.player.McstatsCommand;
 import com.gmail.nossr50.commands.player.MctopCommand;
 import com.gmail.nossr50.commands.player.MctopCommand;
-import com.gmail.nossr50.commands.skills.AcrobaticsCommand;
-import com.gmail.nossr50.commands.skills.AlchemyCommand;
-import com.gmail.nossr50.commands.skills.ArcheryCommand;
-import com.gmail.nossr50.commands.skills.AxesCommand;
-import com.gmail.nossr50.commands.skills.ExcavationCommand;
-import com.gmail.nossr50.commands.skills.FishingCommand;
-import com.gmail.nossr50.commands.skills.HerbalismCommand;
-import com.gmail.nossr50.commands.skills.MiningCommand;
-import com.gmail.nossr50.commands.skills.RepairCommand;
-import com.gmail.nossr50.commands.skills.SalvageCommand;
-import com.gmail.nossr50.commands.skills.SmeltingCommand;
-import com.gmail.nossr50.commands.skills.SwordsCommand;
-import com.gmail.nossr50.commands.skills.TamingCommand;
-import com.gmail.nossr50.commands.skills.UnarmedCommand;
-import com.gmail.nossr50.commands.skills.WoodcuttingCommand;
 import com.gmail.nossr50.config.Config;
 import com.gmail.nossr50.config.Config;
 import com.gmail.nossr50.datatypes.skills.PrimarySkill;
 import com.gmail.nossr50.datatypes.skills.PrimarySkill;
 import com.gmail.nossr50.locale.LocaleLoader;
 import com.gmail.nossr50.locale.LocaleLoader;
@@ -170,6 +156,15 @@ public final class CommandRegistrationManager {
         command.setExecutor(new McgodCommand());
         command.setExecutor(new McgodCommand());
     }
     }
 
 
+    private static void registerMmoInfoCommand() {
+        PluginCommand command = mcMMO.p.getCommand("mmoinfo");
+        command.setDescription(LocaleLoader.getString("Commands.Description.mmoinfo"));
+        command.setPermission("mcmmo.commands.mmoinfo");
+        command.setPermissionMessage(permissionsMessage);
+        command.setUsage(LocaleLoader.getString("Commands.Usage.1", "mmoinfo", "[" + LocaleLoader.getString("Commands.Usage.SubSkill") + "]"));
+        command.setExecutor(new MmoInfo());
+    }
+
     private static void registerMcChatSpyCommand() {
     private static void registerMcChatSpyCommand() {
         PluginCommand command = mcMMO.p.getCommand("mcchatspy");
         PluginCommand command = mcMMO.p.getCommand("mcchatspy");
         command.setDescription(LocaleLoader.getString("Commands.Description.mcchatspy"));
         command.setDescription(LocaleLoader.getString("Commands.Description.mcchatspy"));
@@ -441,6 +436,7 @@ public final class CommandRegistrationManager {
 
 
     public static void registerCommands() {
     public static void registerCommands() {
         // Generic Commands
         // Generic Commands
+        registerMmoInfoCommand();
         registerMcImportCommand();
         registerMcImportCommand();
         registerKrakenCommand();
         registerKrakenCommand();
         registerMcabilityCommand();
         registerMcabilityCommand();

+ 13 - 0
src/main/java/com/gmail/nossr50/util/skills/SkillUtils.java

@@ -311,6 +311,19 @@ public class SkillUtils {
         }
         }
     }
     }
 
 
+    public static double getChanceOfSuccess(int skillLevel, double maxLevelBonus, double curve)
+    {
+        return getChanceOfSuccess((double) skillLevel, maxLevelBonus, curve);
+    }
+
+    public static double getChanceOfSuccess(double skillLevel, double maxLevelBonus, double curve)
+    {
+        if(skillLevel > maxLevelBonus)
+            return maxLevelBonus / curve;
+
+       return skillLevel / curve;
+    }
+
     /* NEW VERSION */
     /* NEW VERSION */
     public static boolean isActivationSuccessful(SkillActivationType skillActivationType, AbstractSubSkill abstractSubSkill, Player player, double maxChance, int maxBonusLevel)
     public static boolean isActivationSuccessful(SkillActivationType skillActivationType, AbstractSubSkill abstractSubSkill, Player player, double maxChance, int maxBonusLevel)
     {
     {

+ 11 - 1
src/main/resources/locale/locale_en_US.properties

@@ -40,10 +40,12 @@ JSON.Acrobatics.SubSkill.Roll.Details.Tips=If you hold sneak while falling you c
 Acrobatics.Ability.Proc=[[GREEN]]**Graceful Landing**
 Acrobatics.Ability.Proc=[[GREEN]]**Graceful Landing**
 Acrobatics.Combat.Proc=[[GREEN]]**Dodged**
 Acrobatics.Combat.Proc=[[GREEN]]**Dodged**
 Acrobatics.DodgeChance=[[RED]]Dodge Chance: [[YELLOW]]{0}
 Acrobatics.DodgeChance=[[RED]]Dodge Chance: [[YELLOW]]{0}
+Acrobatics.SubSkill.Roll.Stats=[[GOLD]]Roll Chance [[YELLOW]]{0}%[[GOLD]] Graceful Roll Chance[[YELLOW]] {1}%
 Acrobatics.SubSkill.Roll.Name=Roll
 Acrobatics.SubSkill.Roll.Name=Roll
-Acrobatics.SubSkill.Roll.Description=Reduces or Negates fall damage
+Acrobatics.SubSkill.Roll.Description=Land strategically to avoid damage.
 Acrobatics.SubSkill.Roll.Chance=[[RED]]Roll Chance: [[YELLOW]]{0}
 Acrobatics.SubSkill.Roll.Chance=[[RED]]Roll Chance: [[YELLOW]]{0}
 Acrobatics.SubSkill.Roll.GraceChance=[[RED]]Graceful Roll Chance: [[YELLOW]]{0}
 Acrobatics.SubSkill.Roll.GraceChance=[[RED]]Graceful Roll Chance: [[YELLOW]]{0}
+Acrobatics.SubSkill.Roll.Mechanics=[[GRAY]]Rolling is an active Sub-Skill with a passive component.\nWhenever you take fall damage you have a chance to completely negate the damage based on your skill level, at level 50 you have a [[YELLOW]]{0}%[[GRAY]] chance to prevent damage, and [[YELLOW]]{1}%[[GRAY]] if you activate Graceful Roll.\nThe chance for success is scaled against your skill level in a linear curve until level [[YELLOW]]{2}[[GRAY]] where it maxes out, every level in Acrobatics gives you a [[YELLOW]]{3}%[[GRAY]] chance to succeed.\nBy holding the sneak button you can double your odds to avoid fall damage and avoid up to twice the fall damage! Holding sneak will transform a normal roll into a Graceful Roll.\nRolling will only prevent up to [[RED]]{4}[[GRAY]] damage.
 Acrobatics.SubSkill.GracefulRoll.Name=Graceful Roll
 Acrobatics.SubSkill.GracefulRoll.Name=Graceful Roll
 Acrobatics.SubSkill.GracefulRoll.Description=Twice as effective as a normal Roll
 Acrobatics.SubSkill.GracefulRoll.Description=Twice as effective as a normal Roll
 Acrobatics.SubSkill.Dodge.Name=Dodge
 Acrobatics.SubSkill.Dodge.Name=Dodge
@@ -607,7 +609,15 @@ Commands.Usage.Password=password
 Commands.Usage.Player=player
 Commands.Usage.Player=player
 Commands.Usage.Rate=rate
 Commands.Usage.Rate=rate
 Commands.Usage.Skill=skill
 Commands.Usage.Skill=skill
+Commands.Usage.SubSkill=subskill
 Commands.Usage.XP=xp
 Commands.Usage.XP=xp
+Commands.MmoInfo.NoMatch=That subskill doesn't exist!
+Commands.MmoInfo.Header=[[DARK_AQUA]]-=[]=====[][[GOLD]] MMO Info [[DARK_AQUA]][]=====[]=-
+Commands.MmoInfo.SubSkillHeader=[[GOLD]]Name:[[YELLOW]] {0} 
+Commands.MmoInfo.DetailsHeader=[[DARK_AQUA]]-=[]=====[][[GREEN]] Details [[DARK_AQUA]][]=====[]=-
+Commands.MmoInfo.OldSkill=[[GRAY]]mcMMO skills are being converted into an improved modular skill system, unfortunately this skill has not been converted yet and lacks detailed stats. The new system will allow for faster release times for new mcMMO skills and greater flexibility with existing skills.
+Commands.MmoInfo.Mechanics=[[DARK_AQUA]]-=[]=====[][[GOLD]] Mechanics [[DARK_AQUA]][]=====[]=-
+Commands.MmoInfo.Stats=[[RED]]STATS: {0}
 mcMMO.NoInvites=[[RED]]You have no invites at this time
 mcMMO.NoInvites=[[RED]]You have no invites at this time
 mcMMO.NoPermission=[[DARK_RED]]Insufficient permissions.
 mcMMO.NoPermission=[[DARK_RED]]Insufficient permissions.
 mcMMO.NoSkillNote=[[DARK_GRAY]]If you don't have access to a skill it will not be shown here.
 mcMMO.NoSkillNote=[[DARK_GRAY]]If you don't have access to a skill it will not be shown here.

+ 6 - 1
src/main/resources/plugin.yml

@@ -12,13 +12,17 @@ description: >
 
 
 author: nossr50
 author: nossr50
 authors: [GJ, NuclearW, bm01, Glitchfinder, TfT_02, t00thpick1, Riking]
 authors: [GJ, NuclearW, bm01, Glitchfinder, TfT_02, t00thpick1, Riking]
-website: https://mcmmo.org
+website: https://www.mcmmo.org
 main: com.gmail.nossr50.mcMMO
 main: com.gmail.nossr50.mcMMO
 softdepend: [CombatTag, HealthBar]
 softdepend: [CombatTag, HealthBar]
 load: STARTUP
 load: STARTUP
 api-version: 1.13
 api-version: 1.13
 
 
 commands:
 commands:
+    mmoinfo:
+        aliases: [mcinfo]
+        description: Info pages for mcMMO
+        permission: mcmmo.commands.mmoinfo
     xprate:
     xprate:
         aliases: [mcxprate]
         aliases: [mcxprate]
         description: Modify the xp rate or start an event
         description: Modify the xp rate or start an event
@@ -750,6 +754,7 @@ permissions:
     mcmmo.commands.defaults:
     mcmmo.commands.defaults:
         description: Implies all default mcmmo.commands permissions.
         description: Implies all default mcmmo.commands permissions.
         children:
         children:
+            mcmmo.commands.mmoinfo: true
             mcmmo.commands.acrobatics: true
             mcmmo.commands.acrobatics: true
             mcmmo.commands.alchemy: true
             mcmmo.commands.alchemy: true
             mcmmo.commands.archery: true
             mcmmo.commands.archery: true