瀏覽代碼

tridents/xbows WIP part 1

nossr50 5 年之前
父節點
當前提交
ea448dfa47
共有 29 個文件被更改,包括 622 次插入36 次删除
  1. 31 0
      Changelog.txt
  2. 1 1
      pom.xml
  3. 33 0
      src/main/java/com/gmail/nossr50/commands/skills/CrossbowsCommand.java
  4. 33 0
      src/main/java/com/gmail/nossr50/commands/skills/TridentsCommand.java
  5. 1 1
      src/main/java/com/gmail/nossr50/config/skills/alchemy/PotionConfig.java
  6. 3 3
      src/main/java/com/gmail/nossr50/database/DatabaseManagerFactory.java
  7. 204 8
      src/main/java/com/gmail/nossr50/database/FlatfileDatabaseManager.java
  8. 6 3
      src/main/java/com/gmail/nossr50/database/SQLDatabaseManager.java
  9. 11 1
      src/main/java/com/gmail/nossr50/datatypes/player/McMMOPlayer.java
  10. 12 2
      src/main/java/com/gmail/nossr50/datatypes/player/PlayerProfile.java
  11. 6 2
      src/main/java/com/gmail/nossr50/datatypes/skills/PrimarySkillType.java
  12. 9 1
      src/main/java/com/gmail/nossr50/datatypes/skills/SubSkillType.java
  13. 3 3
      src/main/java/com/gmail/nossr50/party/PartyManager.java
  14. 2 2
      src/main/java/com/gmail/nossr50/runnables/backups/CleanBackupsTask.java
  15. 11 0
      src/main/java/com/gmail/nossr50/skills/crossbows/CrossbowManager.java
  16. 13 0
      src/main/java/com/gmail/nossr50/skills/tridents/TridentManager.java
  17. 10 0
      src/main/java/com/gmail/nossr50/util/ItemUtils.java
  18. 21 0
      src/main/java/com/gmail/nossr50/util/MaterialMapStore.java
  19. 37 3
      src/main/java/com/gmail/nossr50/util/experience/ExperienceBarManager.java
  20. 1 1
      src/main/java/com/gmail/nossr50/util/scoreboards/ScoreboardWrapper.java
  21. 30 0
      src/main/java/com/gmail/nossr50/util/skills/CombatUtils.java
  22. 30 0
      src/main/java/com/gmail/nossr50/util/skills/SkillUtils.java
  23. 5 5
      src/main/java/net/shatteredlands/shatt/backup/ZipLibrary.java
  24. 13 0
      src/main/resources/config.yml
  25. 12 0
      src/main/resources/experience.yml
  26. 4 0
      src/main/resources/locale/locale_en_US.properties
  27. 44 0
      src/main/resources/plugin.yml
  28. 34 0
      src/main/resources/skillranks.yml
  29. 2 0
      src/main/resources/tridents.yml

+ 31 - 0
Changelog.txt

@@ -1,3 +1,34 @@
+Version 2.1.133
+    New Skill - Tridents
+    New Skill - Crossbows
+
+    New config file named 'tridents.yml'
+
+    New command /tridents - Skill Info for Tridents
+    New command /crossbows - Skill Info for Crossbows
+
+    Added Sub-Skill named Multi Tasking to Tridents
+    Added Sub-Skill named Cone of Death to Crossbows
+
+    (These permissions are all included in the mcmmo.defaults node)
+    New permission node 'mcmmo.commands.tridents'
+    New permission node 'mcmmo.skills.tridents'
+    New permission node 'mcmmo.ability.tridents.all'
+    New permission node 'mcmmo.ability.tridents.multitasking'
+    New permission node 'mcmmo.commands.crossbows'
+    New permission node 'mcmmo.skills.crossbows'
+    New permission node 'mcmmo.ability.crossbows.all'
+
+    Added new locale string 'Overhaul.Name.Tridents'
+    Added new locale string 'Overhaul.Name.Crossbows'
+
+    Added XP bar settings for Tridents & Crossbows to experience.yml
+    Changed some debug level logging to info level for convenience
+    XP Bar settings (set by /mmoxpbar command) are now saved per player
+
+    Notes:
+    These are the first new skills that I've written for mcMMO in about 9 years, I'll be listening closely to feedback and tweaking them often.
+
 Version 2.1.132
     A fix is in place to prevent an exploit from working that is due to a yet to be patched Spigot server software bug
     Fixed a NPE that could happen when players swapped items from their hotbar

+ 1 - 1
pom.xml

@@ -2,7 +2,7 @@
     <modelVersion>4.0.0</modelVersion>
     <groupId>com.gmail.nossr50.mcMMO</groupId>
     <artifactId>mcMMO</artifactId>
-    <version>2.1.132</version>
+    <version>2.1.133-SNAPSHOT</version>
     <name>mcMMO</name>
     <url>https://github.com/mcMMO-Dev/mcMMO</url>
     <scm>

+ 33 - 0
src/main/java/com/gmail/nossr50/commands/skills/CrossbowsCommand.java

@@ -0,0 +1,33 @@
+package com.gmail.nossr50.commands.skills;
+
+import com.gmail.nossr50.datatypes.skills.PrimarySkillType;
+import net.md_5.bungee.api.chat.TextComponent;
+import org.bukkit.entity.Player;
+
+import java.util.List;
+
+public class CrossbowsCommand extends SkillCommand {
+    public CrossbowsCommand() {
+        super(PrimarySkillType.CROSSBOWS);
+    }
+
+    @Override
+    protected void dataCalculations(Player player, float skillValue) {
+
+    }
+
+    @Override
+    protected void permissionsCheck(Player player) {
+
+    }
+
+    @Override
+    protected List<String> statsDisplay(Player player, float skillValue, boolean hasEndurance, boolean isLucky) {
+        return null;
+    }
+
+    @Override
+    protected List<TextComponent> getTextComponents(Player player) {
+        return null;
+    }
+}

+ 33 - 0
src/main/java/com/gmail/nossr50/commands/skills/TridentsCommand.java

@@ -0,0 +1,33 @@
+package com.gmail.nossr50.commands.skills;
+
+import com.gmail.nossr50.datatypes.skills.PrimarySkillType;
+import net.md_5.bungee.api.chat.TextComponent;
+import org.bukkit.entity.Player;
+
+import java.util.List;
+
+public class TridentsCommand extends SkillCommand {
+    public TridentsCommand() {
+        super(PrimarySkillType.TRIDENTS);
+    }
+
+    @Override
+    protected void dataCalculations(Player player, float skillValue) {
+
+    }
+
+    @Override
+    protected void permissionsCheck(Player player) {
+
+    }
+
+    @Override
+    protected List<String> statsDisplay(Player player, float skillValue, boolean hasEndurance, boolean isLucky) {
+        return null;
+    }
+
+    @Override
+    protected List<TextComponent> getTextComponents(Player player) {
+        return null;
+    }
+}

+ 1 - 1
src/main/java/com/gmail/nossr50/config/skills/alchemy/PotionConfig.java

@@ -101,7 +101,7 @@ public class PotionConfig extends ConfigLoader {
             }
         }
 
-        mcMMO.p.debug("Loaded " + pass + " Alchemy potions, skipped " + fail + ".");
+        mcMMO.p.getLogger().info("Loaded " + pass + " Alchemy potions, skipped " + fail + ".");
     }
 
     /**

+ 3 - 3
src/main/java/com/gmail/nossr50/database/DatabaseManagerFactory.java

@@ -13,14 +13,14 @@ public class DatabaseManagerFactory {
                 return createDefaultCustomDatabaseManager();
             }
             catch (Exception e) {
-                mcMMO.p.debug("Could not create custom database manager");
+                mcMMO.p.getLogger().info("Could not create custom database manager");
                 e.printStackTrace();
             }
             catch (Throwable e) {
-                mcMMO.p.debug("Failed to create custom database manager");
+                mcMMO.p.getLogger().info("Failed to create custom database manager");
                 e.printStackTrace();
             }
-            mcMMO.p.debug("Falling back on " + (Config.getInstance().getUseMySQL() ? "SQL" : "Flatfile") + " database");
+            mcMMO.p.getLogger().info("Falling back on " + (Config.getInstance().getUseMySQL() ? "SQL" : "Flatfile") + " database");
         }
 
         return Config.getInstance().getUseMySQL() ? new SQLDatabaseManager() : new FlatfileDatabaseManager();

+ 204 - 8
src/main/java/com/gmail/nossr50/database/FlatfileDatabaseManager.java

@@ -14,6 +14,8 @@ import com.gmail.nossr50.mcMMO;
 import com.gmail.nossr50.runnables.database.UUIDUpdateAsyncTask;
 import com.gmail.nossr50.util.Misc;
 import com.gmail.nossr50.util.StringUtils;
+import com.gmail.nossr50.util.experience.ExperienceBarManager;
+import com.gmail.nossr50.util.skills.SkillUtils;
 import org.bukkit.OfflinePlayer;
 
 import java.io.*;
@@ -360,6 +362,55 @@ public final class FlatfileDatabaseManager implements DatabaseManager {
         writer.append(uuid != null ? uuid.toString() : "NULL").append(":");
         writer.append(profile.getScoreboardTipsShown()).append(":");
         writer.append(profile.getUniqueData(UniqueDataType.CHIMAERA_WING_DATS)).append(":");
+
+        /*
+            public static int SKILLS_TRIDENTS = 44;
+            public static int EXP_TRIDENTS = 45;
+            public static int SKILLS_CROSSBOWS = 46;
+            public static int EXP_CROSSBOWS = 47;
+            public static int BARSTATE_ACROBATICS = 48;
+            public static int BARSTATE_ALCHEMY = 49;
+            public static int BARSTATE_ARCHERY = 50;
+            public static int BARSTATE_AXES = 51;
+            public static int BARSTATE_EXCAVATION = 52;
+            public static int BARSTATE_FISHING = 53;
+            public static int BARSTATE_HERBALISM = 54;
+            public static int BARSTATE_MINING = 55;
+            public static int BARSTATE_REPAIR = 56;
+            public static int BARSTATE_SALVAGE = 57;
+            public static int BARSTATE_SMELTING = 58;
+            public static int BARSTATE_SWORDS = 59;
+            public static int BARSTATE_TAMING = 60;
+            public static int BARSTATE_UNARMED = 61;
+            public static int BARSTATE_WOODCUTTING = 62;
+            public static int BARSTATE_TRIDENTS = 63;
+            public static int BARSTATE_CROSSBOWS = 64;
+         */
+
+        writer.append(profile.getSkillLevel(PrimarySkillType.TRIDENTS)).append(":");
+        writer.append(profile.getSkillXpLevel(PrimarySkillType.TRIDENTS)).append(":");
+        writer.append(profile.getSkillLevel(PrimarySkillType.CROSSBOWS)).append(":");
+        writer.append(profile.getSkillXpLevel(PrimarySkillType.CROSSBOWS)).append(":");
+
+        //XPBar States
+        writer.append(profile.getXpBarStateMap().get(PrimarySkillType.ACROBATICS).toString()).append(":");
+        writer.append(profile.getXpBarStateMap().get(PrimarySkillType.ALCHEMY).toString()).append(":");
+        writer.append(profile.getXpBarStateMap().get(PrimarySkillType.ARCHERY).toString()).append(":");
+        writer.append(profile.getXpBarStateMap().get(PrimarySkillType.AXES).toString()).append(":");
+        writer.append(profile.getXpBarStateMap().get(PrimarySkillType.EXCAVATION).toString()).append(":");
+        writer.append(profile.getXpBarStateMap().get(PrimarySkillType.FISHING).toString()).append(":");
+        writer.append(profile.getXpBarStateMap().get(PrimarySkillType.HERBALISM).toString()).append(":");
+        writer.append(profile.getXpBarStateMap().get(PrimarySkillType.MINING).toString()).append(":");
+        writer.append(profile.getXpBarStateMap().get(PrimarySkillType.REPAIR).toString()).append(":");
+        writer.append(profile.getXpBarStateMap().get(PrimarySkillType.SALVAGE).toString()).append(":");
+        writer.append(profile.getXpBarStateMap().get(PrimarySkillType.SMELTING).toString()).append(":");
+        writer.append(profile.getXpBarStateMap().get(PrimarySkillType.SWORDS).toString()).append(":");
+        writer.append(profile.getXpBarStateMap().get(PrimarySkillType.TAMING).toString()).append(":");
+        writer.append(profile.getXpBarStateMap().get(PrimarySkillType.UNARMED).toString()).append(":");
+        writer.append(profile.getXpBarStateMap().get(PrimarySkillType.WOODCUTTING).toString()).append(":");
+        writer.append(profile.getXpBarStateMap().get(PrimarySkillType.TRIDENTS).toString()).append(":");
+        writer.append(profile.getXpBarStateMap().get(PrimarySkillType.CROSSBOWS).toString()).append(":");
+
         writer.append("\r\n");
     }
 
@@ -438,6 +489,27 @@ public final class FlatfileDatabaseManager implements DatabaseManager {
                 out.append("0:"); // AlchemyXp
                 out.append(uuid != null ? uuid.toString() : "NULL").append(":"); // UUID
                 out.append("0:"); // Scoreboard tips shown
+                out.append("0:"); // Chimaera Wing Dats
+
+                //Barstates for the 15 currently existing skills by ordinal value
+                out.append("NORMAL:"); // Acrobatics
+                out.append("NORMAL:"); // Alchemy
+                out.append("NORMAL:"); // Archery
+                out.append("NORMAL:"); // Axes
+                out.append("NORMAL:"); // Excavation
+                out.append("NORMAL:"); // Fishing
+                out.append("NORMAL:"); // Herbalism
+                out.append("NORMAL:"); // Mining
+                out.append("NORMAL:"); // Repair
+                out.append("NORMAL:"); // Salvage
+                out.append("NORMAL:"); // Smelting
+                out.append("NORMAL:"); // Swords
+                out.append("NORMAL:"); // Taming
+                out.append("NORMAL:"); // Unarmed
+                out.append("NORMAL:"); // Woodcutting
+                out.append("NORMAL:"); // Tridents
+                out.append("NORMAL:"); // Crossbows
+
                 // Add more in the same format as the line above
 
                 out.newLine();
@@ -495,7 +567,7 @@ public final class FlatfileDatabaseManager implements DatabaseManager {
 
                     // Update playerName in database after name change
                     if (!character[USERNAME].equalsIgnoreCase(playerName)) {
-                        mcMMO.p.debug("Name change detected: " + character[USERNAME] + " => " + playerName);
+                        mcMMO.p.getLogger().info("Name change detected: " + character[USERNAME] + " => " + playerName);
                         character[USERNAME] = playerName;
                     }
 
@@ -902,6 +974,7 @@ public final class FlatfileDatabaseManager implements DatabaseManager {
                             updated = true;
                         }
 
+                        //TODO: If new skills are added this needs to be rewritten
                         if (Config.getInstance().getTruncateSkills()) {
                             for (PrimarySkillType skill : PrimarySkillType.NON_CHILD_SKILLS) {
                                 int index = getSkillIndex(skill);
@@ -909,7 +982,7 @@ public final class FlatfileDatabaseManager implements DatabaseManager {
                                     continue;
                                 }
                                 int cap = Config.getInstance().getLevelCap(skill);
-                                if (Integer.valueOf(character[index]) > cap) {
+                                if (Integer.parseInt(character[index]) > cap) {
                                     mcMMO.p.getLogger().warning("Truncating " + skill.getName() + " to configured max level for player " + character[USERNAME]);
                                     character[index] = cap + "";
                                     updated = true;
@@ -999,15 +1072,85 @@ public final class FlatfileDatabaseManager implements DatabaseManager {
                             // Version 1.5.02
                             character = Arrays.copyOf(character, character.length + 1);
                             character[character.length - 1] = "0";
+
                             if (oldVersion == null) {
                                 oldVersion = "1.5.02";
                             }
                         }
 
+                        if(character.length <= 43) {
+                            // Addition of Chimaera wing DATS
+                            character = Arrays.copyOf(character, character.length + 1);
+                            character[character.length - 1] = "0";
+
+                            if (oldVersion == null) {
+                                oldVersion = "2.1.133";
+                            }
+                        }
+
+                        if(character.length <= 65) {
+
+                            if (oldVersion == null) {
+                                oldVersion = "2.1.133";
+                            }
+
+                            character = Arrays.copyOf(character, 64); // new array size
+
+                            /*
+                                public static int SKILLS_TRIDENTS = 44;
+                                public static int EXP_TRIDENTS = 45;
+                                public static int SKILLS_CROSSBOWS = 46;
+                                public static int EXP_CROSSBOWS = 47;
+                                public static int BARSTATE_ACROBATICS = 48;
+                                public static int BARSTATE_ALCHEMY = 49;
+                                public static int BARSTATE_ARCHERY = 50;
+                                public static int BARSTATE_AXES = 51;
+                                public static int BARSTATE_EXCAVATION = 52;
+                                public static int BARSTATE_FISHING = 53;
+                                public static int BARSTATE_HERBALISM = 54;
+                                public static int BARSTATE_MINING = 55;
+                                public static int BARSTATE_REPAIR = 56;
+                                public static int BARSTATE_SALVAGE = 57;
+                                public static int BARSTATE_SMELTING = 58;
+                                public static int BARSTATE_SWORDS = 59;
+                                public static int BARSTATE_TAMING = 60;
+                                public static int BARSTATE_UNARMED = 61;
+                                public static int BARSTATE_WOODCUTTING = 62;
+                                public static int BARSTATE_TRIDENTS = 63;
+                                public static int BARSTATE_CROSSBOWS = 64;
+                             */
+
+                            character[44-1] = "0"; //trident skill lvl
+                            character[45-1] = "0"; //trident xp value
+                            character[46-1] = "0"; //xbow skill lvl
+                            character[47-1] = "0"; //xbow xp lvl
+
+                            //Barstates 48-64
+                            character[48-1] = "NORMAL";
+                            character[49-1] = "NORMAL";
+                            character[50-1] = "NORMAL";
+                            character[51-1] = "NORMAL";
+                            character[52-1] = "NORMAL";
+                            character[53-1] = "NORMAL";
+                            character[54-1] = "NORMAL";
+                            character[55-1] = "NORMAL";
+                            character[56-1] = "NORMAL";
+                            character[57-1] = "DISABLED"; //Child skills
+                            character[58-1] = "DISABLED"; //Child skills
+                            character[59-1] = "NORMAL";
+                            character[60-1] = "NORMAL";
+                            character[61-1] = "NORMAL";
+                            character[62-1] = "NORMAL";
+                            character[63-1] = "NORMAL";
+                            character[64-1] = "NORMAL";
+                        }
+
                         boolean corrupted = false;
 
                         for (int i = 0; i < character.length; i++) {
+                            //Sigh... this code
                             if (character[i].isEmpty() && !(i == 2 || i == 3 || i == 23 || i == 33 || i == 41)) {
+                                mcMMO.p.getLogger().info("Player data at index "+i+" appears to be empty, possible corruption of data has occurred.");
                                 corrupted = true;
                                 if (i == 37) {
                                     character[i] = String.valueOf(System.currentTimeMillis() / Misc.TIME_CONVERSION_FACTOR);
@@ -1032,11 +1175,11 @@ public final class FlatfileDatabaseManager implements DatabaseManager {
                         }
 
                         if (corrupted) {
-                            mcMMO.p.debug("Updating corrupted database line for player " + character[USERNAME]);
+                            mcMMO.p.getLogger().info("Updating corrupted database line for player " + character[USERNAME]);
                         }
 
                         if (oldVersion != null) {
-                            mcMMO.p.debug("Updating database line from before version " + oldVersion + " for player " + character[USERNAME]);
+                            mcMMO.p.getLogger().info("Updating database line from before version " + oldVersion + " for player " + character[USERNAME]);
                         }
 
                         updated |= corrupted;
@@ -1099,7 +1242,7 @@ public final class FlatfileDatabaseManager implements DatabaseManager {
         usersFile.getParentFile().mkdir();
 
         try {
-            mcMMO.p.debug("Creating mcmmo.users file...");
+            mcMMO.p.getLogger().info("Creating mcmmo.users file...");
             new File(mcMMO.getUsersFilePath()).createNewFile();
         }
         catch (IOException e) {
@@ -1142,6 +1285,7 @@ public final class FlatfileDatabaseManager implements DatabaseManager {
         Map<PrimarySkillType, Float>     skillsXp   = new EnumMap<PrimarySkillType, Float>(PrimarySkillType.class);     // Skill & XP
         Map<SuperAbilityType, Integer> skillsDATS = new EnumMap<SuperAbilityType, Integer>(SuperAbilityType.class); // Ability & Cooldown
         Map<UniqueDataType, Integer> uniquePlayerDataMap = new EnumMap<UniqueDataType, Integer>(UniqueDataType.class);
+        Map<PrimarySkillType, ExperienceBarManager.BarState> xpBarStateMap = new EnumMap<PrimarySkillType, ExperienceBarManager.BarState>(PrimarySkillType.class);
         MobHealthbarType mobHealthbarType;
         int scoreboardTipsShown;
 
@@ -1160,6 +1304,8 @@ public final class FlatfileDatabaseManager implements DatabaseManager {
         skillsXp.put(PrimarySkillType.ACROBATICS, (float) Integer.valueOf(character[EXP_ACROBATICS]));
         skillsXp.put(PrimarySkillType.FISHING, (float) Integer.valueOf(character[EXP_FISHING]));
         skillsXp.put(PrimarySkillType.ALCHEMY, (float) Integer.valueOf(character[EXP_ALCHEMY]));
+        skillsXp.put(PrimarySkillType.TRIDENTS, (float) Integer.valueOf(character[EXP_TRIDENTS]));
+        skillsXp.put(PrimarySkillType.CROSSBOWS, (float) Integer.valueOf(character[EXP_CROSSBOWS]));
 
         // Taming - Unused
         skillsDATS.put(SuperAbilityType.SUPER_BREAKER, Integer.valueOf(character[COOLDOWN_SUPER_BREAKER]));
@@ -1190,7 +1336,7 @@ public final class FlatfileDatabaseManager implements DatabaseManager {
         }
 
         try {
-            scoreboardTipsShown = Integer.valueOf(character[SCOREBOARD_TIPS]);
+            scoreboardTipsShown = Integer.parseInt(character[SCOREBOARD_TIPS]);
         }
         catch (Exception e) {
             scoreboardTipsShown = 0;
@@ -1203,7 +1349,30 @@ public final class FlatfileDatabaseManager implements DatabaseManager {
             uniquePlayerDataMap.put(UniqueDataType.CHIMAERA_WING_DATS, 0);
         }
 
-        return new PlayerProfile(character[USERNAME], uuid, skills, skillsXp, skillsDATS, mobHealthbarType, scoreboardTipsShown, uniquePlayerDataMap);
+        try {
+            xpBarStateMap.put(PrimarySkillType.ACROBATICS, SkillUtils.asBarState(character[BARSTATE_ACROBATICS]));
+            xpBarStateMap.put(PrimarySkillType.ALCHEMY, SkillUtils.asBarState(character[BARSTATE_ALCHEMY]));
+            xpBarStateMap.put(PrimarySkillType.ARCHERY, SkillUtils.asBarState(character[BARSTATE_ARCHERY]));
+            xpBarStateMap.put(PrimarySkillType.AXES, SkillUtils.asBarState(character[BARSTATE_AXES]));
+            xpBarStateMap.put(PrimarySkillType.EXCAVATION, SkillUtils.asBarState(character[BARSTATE_EXCAVATION]));
+            xpBarStateMap.put(PrimarySkillType.FISHING, SkillUtils.asBarState(character[BARSTATE_FISHING]));
+            xpBarStateMap.put(PrimarySkillType.HERBALISM, SkillUtils.asBarState(character[BARSTATE_HERBALISM]));
+            xpBarStateMap.put(PrimarySkillType.MINING, SkillUtils.asBarState(character[BARSTATE_MINING]));
+            xpBarStateMap.put(PrimarySkillType.REPAIR, SkillUtils.asBarState(character[BARSTATE_REPAIR]));
+            xpBarStateMap.put(PrimarySkillType.SALVAGE, SkillUtils.asBarState(character[BARSTATE_SALVAGE]));
+            xpBarStateMap.put(PrimarySkillType.SMELTING, SkillUtils.asBarState(character[BARSTATE_SMELTING]));
+            xpBarStateMap.put(PrimarySkillType.SWORDS, SkillUtils.asBarState(character[BARSTATE_SWORDS]));
+            xpBarStateMap.put(PrimarySkillType.TAMING, SkillUtils.asBarState(character[BARSTATE_TAMING]));
+            xpBarStateMap.put(PrimarySkillType.UNARMED, SkillUtils.asBarState(character[BARSTATE_UNARMED]));
+            xpBarStateMap.put(PrimarySkillType.WOODCUTTING, SkillUtils.asBarState(character[BARSTATE_WOODCUTTING]));
+            xpBarStateMap.put(PrimarySkillType.TRIDENTS, SkillUtils.asBarState(character[BARSTATE_TRIDENTS]));
+            xpBarStateMap.put(PrimarySkillType.CROSSBOWS, SkillUtils.asBarState(character[BARSTATE_CROSSBOWS]));
+
+        } catch (Exception e) {
+            xpBarStateMap = SkillUtils.generateDefaultBarStateMap();
+        }
+
+        return new PlayerProfile(character[USERNAME], uuid, skills, skillsXp, skillsDATS, mobHealthbarType, scoreboardTipsShown, uniquePlayerDataMap, xpBarStateMap);
     }
 
     private Map<PrimarySkillType, Integer> getSkillMapFromLine(String[] character) {
@@ -1222,6 +1391,8 @@ public final class FlatfileDatabaseManager implements DatabaseManager {
         skills.put(PrimarySkillType.ACROBATICS, Integer.valueOf(character[SKILLS_ACROBATICS]));
         skills.put(PrimarySkillType.FISHING, Integer.valueOf(character[SKILLS_FISHING]));
         skills.put(PrimarySkillType.ALCHEMY, Integer.valueOf(character[SKILLS_ALCHEMY]));
+        skills.put(PrimarySkillType.TRIDENTS, Integer.valueOf(character[SKILLS_TRIDENTS]));
+        skills.put(PrimarySkillType.CROSSBOWS, Integer.valueOf(character[SKILLS_CROSSBOWS]));
 
         return skills;
     }
@@ -1261,6 +1432,10 @@ public final class FlatfileDatabaseManager implements DatabaseManager {
                 return SKILLS_UNARMED;
             case WOODCUTTING:
                 return SKILLS_WOODCUTTING;
+            case TRIDENTS:
+                return SKILLS_TRIDENTS;
+            case CROSSBOWS:
+                return SKILLS_CROSSBOWS;
             default:
                 throw new RuntimeException("Primary Skills only");
             
@@ -1307,6 +1482,27 @@ public final class FlatfileDatabaseManager implements DatabaseManager {
     public static int UUID_INDEX = 41;
     public static int SCOREBOARD_TIPS = 42;
     public static int COOLDOWN_CHIMAERA_WING = 43;
+    public static int SKILLS_TRIDENTS = 44;
+    public static int EXP_TRIDENTS = 45;
+    public static int SKILLS_CROSSBOWS = 46;
+    public static int EXP_CROSSBOWS = 47;
+    public static int BARSTATE_ACROBATICS = 48;
+    public static int BARSTATE_ALCHEMY = 49;
+    public static int BARSTATE_ARCHERY = 50;
+    public static int BARSTATE_AXES = 51;
+    public static int BARSTATE_EXCAVATION = 52;
+    public static int BARSTATE_FISHING = 53;
+    public static int BARSTATE_HERBALISM = 54;
+    public static int BARSTATE_MINING = 55;
+    public static int BARSTATE_REPAIR = 56;
+    public static int BARSTATE_SALVAGE = 57;
+    public static int BARSTATE_SMELTING = 58;
+    public static int BARSTATE_SWORDS = 59;
+    public static int BARSTATE_TAMING = 60;
+    public static int BARSTATE_UNARMED = 61;
+    public static int BARSTATE_WOODCUTTING = 62;
+    public static int BARSTATE_TRIDENTS = 63;
+    public static int BARSTATE_CROSSBOWS = 64;
 
     public void resetMobHealthSettings() {
         BufferedReader in = null;
@@ -1328,7 +1524,7 @@ public final class FlatfileDatabaseManager implements DatabaseManager {
                     
                     character[HEALTHBAR] = Config.getInstance().getMobHealthbarDefault().toString();
                     
-                    line = new StringBuilder(org.apache.commons.lang.StringUtils.join(character, ":")).append(":").toString();
+                    line = org.apache.commons.lang.StringUtils.join(character, ":") + ":";
 
                     writer.append(line).append("\r\n");
                 }

+ 6 - 3
src/main/java/com/gmail/nossr50/database/SQLDatabaseManager.java

@@ -13,6 +13,7 @@ import com.gmail.nossr50.datatypes.skills.SuperAbilityType;
 import com.gmail.nossr50.mcMMO;
 import com.gmail.nossr50.runnables.database.UUIDUpdateAsyncTask;
 import com.gmail.nossr50.util.Misc;
+import com.gmail.nossr50.util.experience.ExperienceBarManager;
 import org.apache.tomcat.jdbc.pool.DataSource;
 import org.apache.tomcat.jdbc.pool.PoolProperties;
 import org.bukkit.scheduler.BukkitRunnable;
@@ -963,7 +964,7 @@ public final class SQLDatabaseManager implements DatabaseManager {
      */
     private void checkDatabaseStructure(Connection connection, UpgradeType upgrade) {
         if (!mcMMO.getUpgradeManager().shouldUpgrade(upgrade)) {
-            mcMMO.p.debug("Skipping " + upgrade.name() + " upgrade (unneeded)");
+            mcMMO.p.getLogger().info("Skipping " + upgrade.name() + " upgrade (unneeded)");
             return;
         }
 
@@ -1074,6 +1075,8 @@ public final class SQLDatabaseManager implements DatabaseManager {
         Map<PrimarySkillType, Float> skillsXp = new EnumMap<PrimarySkillType, Float>(PrimarySkillType.class); // Skill & XP
         Map<SuperAbilityType, Integer> skillsDATS = new EnumMap<SuperAbilityType, Integer>(SuperAbilityType.class); // Ability & Cooldown
         Map<UniqueDataType, Integer> uniqueData = new EnumMap<UniqueDataType, Integer>(UniqueDataType.class); //Chimaera wing cooldown and other misc info
+        Map<PrimarySkillType, ExperienceBarManager.BarState> xpBarStateMap = new EnumMap<PrimarySkillType, ExperienceBarManager.BarState>(PrimarySkillType.class);
+
         MobHealthbarType mobHealthbarType;
         UUID uuid;
         int scoreboardTipsShown;
@@ -1148,7 +1151,7 @@ public final class SQLDatabaseManager implements DatabaseManager {
             uuid = null;
         }
 
-        return new PlayerProfile(playerName, uuid, skills, skillsXp, skillsDATS, mobHealthbarType, scoreboardTipsShown, uniqueData);
+        return new PlayerProfile(playerName, uuid, skills, skillsXp, skillsDATS, mobHealthbarType, scoreboardTipsShown, uniqueData, xpBarStateMap);
     }
 
     private void printErrors(SQLException ex) {
@@ -1515,7 +1518,7 @@ public final class SQLDatabaseManager implements DatabaseManager {
 
     @Override
     public void onDisable() {
-        mcMMO.p.debug("Releasing connection pool resource...");
+        mcMMO.p.getLogger().info("Releasing connection pool resource...");
         miscPool.close();
         loadPool.close();
         savePool.close();

+ 11 - 1
src/main/java/com/gmail/nossr50/datatypes/player/McMMOPlayer.java

@@ -27,6 +27,7 @@ import com.gmail.nossr50.skills.alchemy.AlchemyManager;
 import com.gmail.nossr50.skills.archery.ArcheryManager;
 import com.gmail.nossr50.skills.axes.AxesManager;
 import com.gmail.nossr50.skills.child.FamilyTree;
+import com.gmail.nossr50.skills.crossbows.CrossbowManager;
 import com.gmail.nossr50.skills.excavation.ExcavationManager;
 import com.gmail.nossr50.skills.fishing.FishingManager;
 import com.gmail.nossr50.skills.herbalism.HerbalismManager;
@@ -36,6 +37,7 @@ import com.gmail.nossr50.skills.salvage.SalvageManager;
 import com.gmail.nossr50.skills.smelting.SmeltingManager;
 import com.gmail.nossr50.skills.swords.SwordsManager;
 import com.gmail.nossr50.skills.taming.TamingManager;
+import com.gmail.nossr50.skills.tridents.TridentManager;
 import com.gmail.nossr50.skills.unarmed.UnarmedManager;
 import com.gmail.nossr50.skills.woodcutting.WoodcuttingManager;
 import com.gmail.nossr50.util.EventUtils;
@@ -140,7 +142,7 @@ public class McMMOPlayer {
             toolMode.put(toolType, false);
         }
 
-        experienceBarManager = new ExperienceBarManager(this);
+        experienceBarManager = new ExperienceBarManager(this, profile.getXpBarStateMap());
 
         debugMode = false; //Debug mode helps solve support issues, players can toggle it on or off
         attackStrength = 1.0D;
@@ -266,6 +268,14 @@ public class McMMOPlayer {
         return (UnarmedManager) skillManagers.get(PrimarySkillType.UNARMED);
     }
 
+    public TridentManager getTridentManager() {
+        return (TridentManager) skillManagers.get(PrimarySkillType.TRIDENTS);
+    }
+
+    public CrossbowManager getCrossbowManager() {
+        return (CrossbowManager) skillManagers.get(PrimarySkillType.CROSSBOWS);
+    }
+
     public WoodcuttingManager getWoodcuttingManager() {
         return (WoodcuttingManager) skillManagers.get(PrimarySkillType.WOODCUTTING);
     }

+ 12 - 2
src/main/java/com/gmail/nossr50/datatypes/player/PlayerProfile.java

@@ -11,7 +11,9 @@ import com.gmail.nossr50.datatypes.skills.SuperAbilityType;
 import com.gmail.nossr50.mcMMO;
 import com.gmail.nossr50.runnables.player.PlayerProfileSaveTask;
 import com.gmail.nossr50.skills.child.FamilyTree;
+import com.gmail.nossr50.util.experience.ExperienceBarManager;
 import com.gmail.nossr50.util.player.UserManager;
+import com.gmail.nossr50.util.skills.SkillUtils;
 import com.google.common.collect.ImmutableMap;
 
 import java.util.HashMap;
@@ -36,6 +38,7 @@ public class PlayerProfile {
     private final Map<PrimarySkillType, Float>     skillsXp   = new HashMap<PrimarySkillType, Float>();     // Skill & XP
     private final Map<SuperAbilityType, Integer> abilityDATS = new HashMap<SuperAbilityType, Integer>(); // Ability & Cooldown
     private final Map<UniqueDataType, Integer> uniquePlayerData = new HashMap<>(); //Misc data that doesn't fit into other categories (chimaera wing, etc..)
+    private Map<PrimarySkillType, ExperienceBarManager.BarState> xpBarState = new HashMap<>();
 
     // Store previous XP gains for diminished returns
     private DelayQueue<SkillXpGain> gainedSkillsXp = new DelayQueue<SkillXpGain>();
@@ -70,14 +73,16 @@ public class PlayerProfile {
     public PlayerProfile(String playerName, boolean isLoaded) {
         this(playerName);
         this.loaded = isLoaded;
+        this.xpBarState = SkillUtils.generateDefaultBarStateMap();
     }
 
     public PlayerProfile(String playerName, UUID uuid, boolean isLoaded) {
         this(playerName, uuid);
         this.loaded = isLoaded;
+        this.xpBarState = SkillUtils.generateDefaultBarStateMap();
     }
 
-    public PlayerProfile(String playerName, UUID uuid, Map<PrimarySkillType, Integer> levelData, Map<PrimarySkillType, Float> xpData, Map<SuperAbilityType, Integer> cooldownData, MobHealthbarType mobHealthbarType, int scoreboardTipsShown, Map<UniqueDataType, Integer> uniqueProfileData) {
+    public PlayerProfile(String playerName, UUID uuid, Map<PrimarySkillType, Integer> levelData, Map<PrimarySkillType, Float> xpData, Map<SuperAbilityType, Integer> cooldownData, MobHealthbarType mobHealthbarType, int scoreboardTipsShown, Map<UniqueDataType, Integer> uniqueProfileData, Map<PrimarySkillType, ExperienceBarManager.BarState> barStateMap) {
         this.playerName = playerName;
         this.uuid = uuid;
         this.mobHealthbarType = mobHealthbarType;
@@ -87,6 +92,7 @@ public class PlayerProfile {
         skillsXp.putAll(xpData);
         abilityDATS.putAll(cooldownData);
         uniquePlayerData.putAll(uniqueProfileData);
+        xpBarState.putAll(barStateMap);
 
         loaded = true;
     }
@@ -115,7 +121,7 @@ public class PlayerProfile {
         }
 
         // TODO should this part be synchronized?
-        PlayerProfile profileCopy = new PlayerProfile(playerName, uuid, ImmutableMap.copyOf(skills), ImmutableMap.copyOf(skillsXp), ImmutableMap.copyOf(abilityDATS), mobHealthbarType, scoreboardTipsShown, ImmutableMap.copyOf(uniquePlayerData));
+        PlayerProfile profileCopy = new PlayerProfile(playerName, uuid, ImmutableMap.copyOf(skills), ImmutableMap.copyOf(skillsXp), ImmutableMap.copyOf(abilityDATS), mobHealthbarType, scoreboardTipsShown, ImmutableMap.copyOf(uniquePlayerData), ImmutableMap.copyOf(xpBarState));
         changed = !mcMMO.getDatabaseManager().saveUser(profileCopy);
 
         if (changed) {
@@ -439,4 +445,8 @@ public class PlayerProfile {
 
         return sum / parents.size();
     }
+
+    public HashMap<PrimarySkillType, ExperienceBarManager.BarState> getXpBarStateMap() {
+        return (HashMap<PrimarySkillType, ExperienceBarManager.BarState>) xpBarState;
+    }
 }

+ 6 - 2
src/main/java/com/gmail/nossr50/datatypes/skills/PrimarySkillType.java

@@ -9,6 +9,7 @@ import com.gmail.nossr50.skills.acrobatics.AcrobaticsManager;
 import com.gmail.nossr50.skills.alchemy.AlchemyManager;
 import com.gmail.nossr50.skills.archery.ArcheryManager;
 import com.gmail.nossr50.skills.axes.AxesManager;
+import com.gmail.nossr50.skills.crossbows.CrossbowManager;
 import com.gmail.nossr50.skills.excavation.ExcavationManager;
 import com.gmail.nossr50.skills.fishing.FishingManager;
 import com.gmail.nossr50.skills.herbalism.HerbalismManager;
@@ -18,6 +19,7 @@ import com.gmail.nossr50.skills.salvage.SalvageManager;
 import com.gmail.nossr50.skills.smelting.SmeltingManager;
 import com.gmail.nossr50.skills.swords.SwordsManager;
 import com.gmail.nossr50.skills.taming.TamingManager;
+import com.gmail.nossr50.skills.tridents.TridentManager;
 import com.gmail.nossr50.skills.unarmed.UnarmedManager;
 import com.gmail.nossr50.skills.woodcutting.WoodcuttingManager;
 import com.gmail.nossr50.util.Permissions;
@@ -63,7 +65,9 @@ public enum PrimarySkillType {
     UNARMED(UnarmedManager.class, Color.BLACK, SuperAbilityType.BERSERK, ToolType.FISTS,
             ImmutableList.of(SubSkillType.UNARMED_BERSERK, SubSkillType.UNARMED_UNARMED_LIMIT_BREAK, SubSkillType.UNARMED_BLOCK_CRACKER, SubSkillType.UNARMED_ARROW_DEFLECT, SubSkillType.UNARMED_DISARM, SubSkillType.UNARMED_IRON_ARM_STYLE, SubSkillType.UNARMED_IRON_GRIP)),
     WOODCUTTING(WoodcuttingManager.class, Color.OLIVE, SuperAbilityType.TREE_FELLER, ToolType.AXE,
-            ImmutableList.of(SubSkillType.WOODCUTTING_LEAF_BLOWER, SubSkillType.WOODCUTTING_TREE_FELLER, SubSkillType.WOODCUTTING_HARVEST_LUMBER));
+            ImmutableList.of(SubSkillType.WOODCUTTING_LEAF_BLOWER, SubSkillType.WOODCUTTING_TREE_FELLER, SubSkillType.WOODCUTTING_HARVEST_LUMBER)),
+    TRIDENTS(TridentManager.class, Color.TEAL, ImmutableList.of(SubSkillType.TRIDENTS_MULTI_TASKING)),
+    CROSSBOWS(CrossbowManager.class, Color.ORANGE, ImmutableList.of(SubSkillType.CROSSBOWS_CONE_OF_DEATH));
 
     private Class<? extends SkillManager> managerClass;
     private Color skillColor;
@@ -77,7 +81,7 @@ public enum PrimarySkillType {
     public static final List<PrimarySkillType> CHILD_SKILLS;
     public static final List<PrimarySkillType> NON_CHILD_SKILLS;
 
-    public static final List<PrimarySkillType> COMBAT_SKILLS = ImmutableList.of(ARCHERY, AXES, SWORDS, TAMING, UNARMED);
+    public static final List<PrimarySkillType> COMBAT_SKILLS = ImmutableList.of(ARCHERY, AXES, SWORDS, TAMING, UNARMED, TRIDENTS, CROSSBOWS);
     public static final List<PrimarySkillType> GATHERING_SKILLS = ImmutableList.of(EXCAVATION, FISHING, HERBALISM, MINING, WOODCUTTING);
     public static final List<PrimarySkillType> MISC_SKILLS = ImmutableList.of(ACROBATICS, ALCHEMY, REPAIR, SALVAGE, SMELTING);
 

+ 9 - 1
src/main/java/com/gmail/nossr50/datatypes/skills/SubSkillType.java

@@ -105,7 +105,15 @@ public enum SubSkillType {
     WOODCUTTING_LEAF_BLOWER(1),
 /*    WOODCUTTING_NATURES_BOUNTY(3),
     WOODCUTTING_SPLINTER(3),*/
-    WOODCUTTING_TREE_FELLER(1);
+    WOODCUTTING_TREE_FELLER(1),
+
+    /* TRIDENTS */
+    TRIDENTS_MULTI_TASKING(5),
+
+    /* CROSSBOWS */
+
+    CROSSBOWS_CONE_OF_DEATH(8);
+
 
     private final int numRanks;
     //TODO: SuperAbilityType should also contain flags for active by default? Not sure if it should work that way.

+ 3 - 3
src/main/java/com/gmail/nossr50/party/PartyManager.java

@@ -628,7 +628,7 @@ public final class PartyManager {
                 parties.add(party);
             }
 
-            mcMMO.p.debug("Loaded (" + parties.size() + ") Parties...");
+            mcMMO.p.getLogger().info("Loaded (" + parties.size() + ") Parties...");
 
             for (Party party : hasAlly) {
                 party.setAlly(PartyManager.getParty(partiesFile.getString(party.getName() + ".Ally")));
@@ -653,7 +653,7 @@ public final class PartyManager {
 
         YamlConfiguration partiesFile = new YamlConfiguration();
 
-        mcMMO.p.debug("Saving Parties... (" + parties.size() + ")");
+        mcMMO.p.getLogger().info("Saving Parties... (" + parties.size() + ")");
         for (Party party : parties) {
             String partyName = party.getName();
             PartyLeader leader = party.getLeader();
@@ -751,7 +751,7 @@ public final class PartyManager {
             parties.add(party);
         }
 
-        mcMMO.p.debug("Loaded (" + parties.size() + ") Parties...");
+        mcMMO.p.getLogger().info("Loaded (" + parties.size() + ") Parties...");
 
         for (Party party : hasAlly) {
             party.setAlly(PartyManager.getParty(partiesFile.getString(party.getName() + ".Ally")));

+ 2 - 2
src/main/java/com/gmail/nossr50/runnables/backups/CleanBackupsTask.java

@@ -38,7 +38,7 @@ public class CleanBackupsTask extends BukkitRunnable {
             Date date = getDate(fileName.split("[.]")[0]);
 
             if (!fileName.contains(".zip") || date == null) {
-                mcMMO.p.debug("Could not determine date for file: " + fileName);
+                mcMMO.p.getLogger().info("Could not determine date for file: " + fileName);
                 continue;
             }
 
@@ -83,7 +83,7 @@ public class CleanBackupsTask extends BukkitRunnable {
 
         for (File file : toDelete) {
             if (file.delete()) {
-                mcMMO.p.debug("Deleted: " + file.getName());
+                mcMMO.p.getLogger().info("Deleted: " + file.getName());
             }
         }
     }

+ 11 - 0
src/main/java/com/gmail/nossr50/skills/crossbows/CrossbowManager.java

@@ -0,0 +1,11 @@
+package com.gmail.nossr50.skills.crossbows;
+
+import com.gmail.nossr50.datatypes.player.McMMOPlayer;
+import com.gmail.nossr50.datatypes.skills.PrimarySkillType;
+import com.gmail.nossr50.skills.SkillManager;
+
+public class CrossbowManager extends SkillManager {
+    public CrossbowManager(McMMOPlayer mcMMOPlayer) {
+        super(mcMMOPlayer, PrimarySkillType.CROSSBOWS);
+    }
+}

+ 13 - 0
src/main/java/com/gmail/nossr50/skills/tridents/TridentManager.java

@@ -0,0 +1,13 @@
+package com.gmail.nossr50.skills.tridents;
+
+import com.gmail.nossr50.datatypes.player.McMMOPlayer;
+import com.gmail.nossr50.datatypes.skills.PrimarySkillType;
+import com.gmail.nossr50.skills.SkillManager;
+
+public class TridentManager extends SkillManager {
+    public TridentManager(McMMOPlayer mcMMOPlayer) {
+        super(mcMMOPlayer, PrimarySkillType.TRIDENTS);
+    }
+
+
+}

+ 10 - 0
src/main/java/com/gmail/nossr50/util/ItemUtils.java

@@ -25,6 +25,11 @@ public final class ItemUtils {
         return mcMMO.getMaterialMapStore().isBow(item.getType().getKey().getKey());
     }
 
+    public static boolean isCrossbow(ItemStack itemStack) {
+        return mcMMO.getMaterialMapStore().isCrossbow(itemStack.getType().getKey().getKey());
+    }
+
+
     public static boolean hasItemInEitherHand(Player player, Material material) {
         return player.getInventory().getItemInMainHand().getType() == material || player.getInventory().getItemInOffHand().getType() == material;
     }
@@ -39,6 +44,11 @@ public final class ItemUtils {
         return mcMMO.getMaterialMapStore().isSword(item.getType().getKey().getKey());
     }
 
+    public static boolean isTrident(ItemStack itemStack) {
+        return mcMMO.getMaterialMapStore().isTrident(itemStack.getType().getKey().getKey());
+    }
+
+
     /**
      * Checks if the item is a hoe.
      *

+ 21 - 0
src/main/java/com/gmail/nossr50/util/MaterialMapStore.java

@@ -48,6 +48,7 @@ public class MaterialMapStore {
     private HashSet<String> pickAxes;
     private HashSet<String> tridents;
     private HashSet<String> bows;
+    private HashSet<String> xbows;
     private HashSet<String> tools;
 
     private HashSet<String> enchantables;
@@ -85,6 +86,7 @@ public class MaterialMapStore {
         diamondTools = new HashSet<>();
         netheriteTools = new HashSet<>();
         bows = new HashSet<>();
+        xbows = new HashSet<>();
         stringTools = new HashSet<>();
         tools = new HashSet<>();
 
@@ -255,6 +257,7 @@ public class MaterialMapStore {
         fillTridents();
         fillStringTools();
         fillBows();
+        fillCrossBows();
 
         //Tools collection
         tools.addAll(woodTools);
@@ -266,12 +269,17 @@ public class MaterialMapStore {
         tools.addAll(tridents);
         tools.addAll(stringTools);
         tools.addAll(bows);
+        tools.addAll(xbows);
     }
 
     private void fillBows() {
         bows.add("bow");
     }
 
+    private void fillCrossBows() {
+        xbows.add("crossbow");
+    }
+
     private void fillStringTools() {
         stringTools.add("bow");
         stringTools.add("fishing_rod");
@@ -598,6 +606,15 @@ public class MaterialMapStore {
         return bows.contains(id);
     }
 
+    public boolean isCrossbow(Material material) {
+        return isCrossbow(material.getKey().getKey());
+    }
+
+    public boolean isCrossbow(String id) {
+        return xbows.contains(id);
+    }
+
+
     public boolean isLeatherArmor(Material material) {
         return isLeatherArmor(material.getKey().getKey());
     }
@@ -686,6 +703,10 @@ public class MaterialMapStore {
         return diamondTools.contains(id);
     }
 
+    public boolean isTrident(Material material) { return isTrident(material.getKey().getKey()); }
+
+    public boolean isTrident(String id) { return tridents.contains(id); }
+
     public boolean isSword(Material material) {
         return isSword(material.getKey().getKey());
     }

+ 37 - 3
src/main/java/com/gmail/nossr50/util/experience/ExperienceBarManager.java

@@ -6,12 +6,14 @@ import com.gmail.nossr50.datatypes.skills.PrimarySkillType;
 import com.gmail.nossr50.mcMMO;
 import com.gmail.nossr50.runnables.skills.ExperienceBarHideTask;
 import com.gmail.nossr50.util.player.NotificationManager;
+import com.gmail.nossr50.util.skills.SkillUtils;
 import org.bukkit.plugin.Plugin;
 import org.jetbrains.annotations.NotNull;
 import org.jetbrains.annotations.Nullable;
 
 import java.util.HashMap;
 import java.util.HashSet;
+import java.util.Map;
 
 /**
  * ExperienceBarManager handles displaying and updating mcMMO experience bars for players
@@ -24,12 +26,16 @@ public class ExperienceBarManager {
     private HashMap<PrimarySkillType, ExperienceBarWrapper> experienceBars;
     private HashMap<PrimarySkillType, ExperienceBarHideTask> experienceBarHideTaskHashMap;
 
+    private final HashMap<PrimarySkillType, BarState> barStateMap;
+
     private HashSet<PrimarySkillType> alwaysVisible;
     private HashSet<PrimarySkillType> disabledBars;
 
-    public ExperienceBarManager(McMMOPlayer mcMMOPlayer)
+    public ExperienceBarManager(McMMOPlayer mcMMOPlayer, HashMap<PrimarySkillType, BarState> barStateMap)
     {
         this.mcMMOPlayer = mcMMOPlayer;
+        this.barStateMap = barStateMap;
+
         init();
     }
 
@@ -41,6 +47,28 @@ public class ExperienceBarManager {
         //Init sets
         alwaysVisible = new HashSet<>();
         disabledBars = new HashSet<>();
+
+        syncBarStates();
+    }
+
+    private void syncBarStates() {
+        for(Map.Entry<PrimarySkillType, BarState> entry : barStateMap.entrySet()) {
+            PrimarySkillType key = entry.getKey();
+            BarState barState = entry.getValue();
+
+            switch(barState) {
+                case NORMAL:
+                    break;
+                case ALWAYS_ON:
+                    xpBarSettingToggle(XPBarSettingTarget.SHOW, key);
+                case DISABLED:
+                    xpBarSettingToggle(XPBarSettingTarget.HIDE, key);
+            }
+        }
+    }
+
+    private void resetBarStateMap() {
+        SkillUtils.setBarStateDefaults(barStateMap);
     }
 
     public void updateExperienceBar(PrimarySkillType primarySkillType, Plugin plugin)
@@ -112,6 +140,7 @@ public class ExperienceBarManager {
                 }
 
                 updateExperienceBar(skillType, mcMMO.p);
+                barStateMap.put(skillType, BarState.ALWAYS_ON);
                 break;
             case HIDE:
                 alwaysVisible.remove(skillType);
@@ -123,6 +152,7 @@ public class ExperienceBarManager {
                 }
 
                 hideExperienceBar(skillType);
+                barStateMap.put(skillType, BarState.DISABLED);
                 break;
             case RESET:
                 resetBarSettings();
@@ -138,12 +168,14 @@ public class ExperienceBarManager {
             hideExperienceBar(permanent);
         }
 
+        resetBarStateMap();
+
         alwaysVisible.clear();
         disabledBars.clear();
 
         //Hide child skills by default
-        disabledBars.add(PrimarySkillType.SALVAGE);
-        disabledBars.add(PrimarySkillType.SMELTING);
+        xpBarSettingToggle(XPBarSettingTarget.HIDE, PrimarySkillType.SALVAGE);
+        xpBarSettingToggle(XPBarSettingTarget.HIDE, PrimarySkillType.SMELTING);
     }
 
     private void informPlayer(@NotNull ExperienceBarManager.@NotNull XPBarSettingTarget settingTarget, @Nullable PrimarySkillType skillType) {
@@ -156,4 +188,6 @@ public class ExperienceBarManager {
     }
 
     public enum XPBarSettingTarget { SHOW, HIDE, RESET, DISABLE }
+
+    public enum BarState { NORMAL, ALWAYS_ON, DISABLED }
 }

+ 1 - 1
src/main/java/com/gmail/nossr50/util/scoreboards/ScoreboardWrapper.java

@@ -245,7 +245,7 @@ public class ScoreboardWrapper {
                 oldBoard = null;
             }
             else {
-                mcMMO.p.debug("Not reverting targetBoard for " + playerName + " - targetBoard was changed by another plugin (Consider disabling the mcMMO scoreboards if you don't want them!)");
+                mcMMO.p.getLogger().info("Not reverting targetBoard for " + playerName + " - targetBoard was changed by another plugin (Consider disabling the mcMMO scoreboards if you don't want them!)");
             }
         }
 

+ 30 - 0
src/main/java/com/gmail/nossr50/util/skills/CombatUtils.java

@@ -18,6 +18,7 @@ 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.TridentManager;
 import com.gmail.nossr50.skills.unarmed.UnarmedManager;
 import com.gmail.nossr50.util.*;
 import com.gmail.nossr50.util.player.NotificationManager;
@@ -90,6 +91,26 @@ public final class CombatUtils {
         processCombatXP(mcMMOPlayer, target, PrimarySkillType.SWORDS);
     }
 
+    public static void processTridentCombat(LivingEntity target, Player player, EntityDamageByEntityEvent entityDamageByEntityEvent) {
+        if (entityDamageByEntityEvent.getCause() == DamageCause.THORNS) {
+            return;
+        }
+
+        McMMOPlayer mcMMOPlayer = UserManager.getPlayer(player);
+
+        //Make sure the profiles been loaded
+        if(mcMMOPlayer == null) {
+            return;
+        }
+
+        TridentManager tridentManager = mcMMOPlayer.getTridentManager();
+//        double initialDamage = entityDamageByEntityEvent.getDamage();
+//        double finalDamage = initialDamage;
+
+        processCombatXP(mcMMOPlayer, target, PrimarySkillType.TRIDENTS);
+    }
+
+
 //    public static void strengthDebug(Player player) {
 //        BukkitPlatform bukkitPlatform = (BukkitPlatform) mcMMO.getPlatformManager().getPlatform();
 //        Bukkit.broadcastMessage("Strength: "+bukkitPlatform.getPlayerAttackStrength(player));
@@ -387,6 +408,15 @@ public final class CombatUtils {
                     processUnarmedCombat(target, player, event);
                 }
             }
+            else if (ItemUtils.isTrident(heldItem)) {
+                if(!PrimarySkillType.TRIDENTS.shouldProcess(target)) {
+                    return;
+                }
+
+                if(PrimarySkillType.TRIDENTS.getPermissions(player)) {
+                    processTridentCombat(target, player, event);
+                }
+            }
         }
 
         else if (entityType == EntityType.WOLF) {

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

@@ -15,6 +15,7 @@ import com.gmail.nossr50.mcMMO;
 import com.gmail.nossr50.util.ItemUtils;
 import com.gmail.nossr50.util.Misc;
 import com.gmail.nossr50.util.StringUtils;
+import com.gmail.nossr50.util.experience.ExperienceBarManager;
 import com.gmail.nossr50.util.player.NotificationManager;
 import com.gmail.nossr50.util.player.UserManager;
 import org.bukkit.Bukkit;
@@ -31,6 +32,7 @@ import org.bukkit.potion.PotionEffect;
 import org.bukkit.potion.PotionEffectType;
 
 import java.util.ArrayList;
+import java.util.HashMap;
 import java.util.Iterator;
 import java.util.List;
 
@@ -44,6 +46,34 @@ public class SkillUtils {
         mcMMOPlayer.beginXpGain(skill, xp, xpGainReason, xpGainSource);
     }
 
+    public static HashMap<PrimarySkillType, ExperienceBarManager.BarState> generateDefaultBarStateMap() {
+        HashMap<PrimarySkillType, ExperienceBarManager.BarState> barStateHashMap = new HashMap<>();
+
+        setBarStateDefaults(barStateHashMap);
+
+        return barStateHashMap;
+    }
+
+    public static ExperienceBarManager.BarState asBarState(String str) {
+        for(ExperienceBarManager.BarState barState : ExperienceBarManager.BarState.values()) {
+            if(barState.toString().equalsIgnoreCase(str)) {
+                return barState;
+            }
+        }
+
+        return ExperienceBarManager.BarState.NORMAL;
+    }
+
+    public static void setBarStateDefaults(HashMap<PrimarySkillType, ExperienceBarManager.BarState> barStateHashMap) {
+        for(PrimarySkillType skillType : PrimarySkillType.values()) {
+            if(skillType.isChildSkill()) {
+                barStateHashMap.put(skillType, ExperienceBarManager.BarState.DISABLED);
+            } else {
+                barStateHashMap.put(skillType, ExperienceBarManager.BarState.NORMAL);
+            }
+        }
+    }
+
     /*
      * Skill Stat Calculations
      */

+ 5 - 5
src/main/java/net/shatteredlands/shatt/backup/ZipLibrary.java

@@ -28,13 +28,13 @@ public class ZipLibrary {
 
     public static void mcMMOBackup() throws IOException {
         if (Config.getInstance().getUseMySQL()) {
-            mcMMO.p.debug("This server is running in SQL Mode.");
-            mcMMO.p.debug("Only config files will be backed up.");
+            mcMMO.p.getLogger().info("This server is running in SQL Mode.");
+            mcMMO.p.getLogger().info("Only config files will be backed up.");
         }
 
         try {
             if (BACKUP_DIR.mkdir()) {
-                mcMMO.p.debug("Created Backup Directory.");
+                mcMMO.p.getLogger().info("Created Backup Directory.");
             }
         }
         catch (Exception e) {
@@ -61,7 +61,7 @@ public class ZipLibrary {
         }
 
         // Actually do something
-        mcMMO.p.debug("Backing up your mcMMO Configuration... ");
+        mcMMO.p.getLogger().info("Backing up your mcMMO Configuration... ");
 
         packZip(fileZip, sources);
     }
@@ -81,7 +81,7 @@ public class ZipLibrary {
 
         zipOut.flush();
         zipOut.close();
-        mcMMO.p.debug("Backup Completed.");
+        mcMMO.p.getLogger().info("Backup Completed.");
     }
 
     private static String buildPath(String path, String file) {

+ 13 - 0
src/main/resources/config.yml

@@ -191,6 +191,8 @@ Hardcore:
             Taming: false
             Unarmed: false
             Woodcutting: false
+            Tridents: false
+            Crossbows: false
     Vampirism:
         Leech_Percentage: 5.0
         Level_Threshold: 0
@@ -208,6 +210,8 @@ Hardcore:
             Taming: false
             Unarmed: false
             Woodcutting: false
+            Tridents: false
+            Crossbows: false
 
 #
 #  Settings for SMP Mods
@@ -415,6 +419,15 @@ Skills:
     Woodcutting:
         Tree_Feller_Sounds: true
         Level_Cap: 0
+    Tridents:
+        Enabled_For_PVP: true
+        Enabled_For_PVE: true
+        Level_Cap: 0
+    Crossbows:
+        Enabled_For_PVP: true
+        Enabled_For_PVE: true
+        Level_Cap: 0
+
 
 #
 #  Settings for Double Drops

+ 12 - 0
src/main/resources/experience.yml

@@ -110,6 +110,14 @@ Experience_Bars:
         Enable: true
         Color: GREEN
         BarStyle: SEGMENTED_6
+    Tridents:
+        Enable: true
+        Color: BLUE
+        BarStyle: SEGMENTED_6
+    Crossbows:
+        Enable: true
+        Color: BLUE
+        BarStyle: SEGMENTED_6
 #
 #  Settings for XP formula
 ###
@@ -159,6 +167,8 @@ Experience_Formula:
         Repair: 1.0
         Fishing: 1.0
         Alchemy: 1.0
+        Tridents: 1.0
+        Crossbows: 1.0
 
     # XP earned by players with the permission mcmmo.perks.xp.customboost.<skillname> will get multiplied
     # with 1.25 by default, resulting in a 25% XP boost
@@ -191,6 +201,8 @@ Diminished_Returns:
         Repair: 20000
         Fishing: 20000
         Alchemy: 20000
+        Tridents: 20000
+        Crossbows: 20000
 
     Time_Interval: 10
 

+ 4 - 0
src/main/resources/locale/locale_en_US.properties

@@ -98,6 +98,8 @@ Overhaul.Name.Swords=Swords
 Overhaul.Name.Taming=Taming
 Overhaul.Name.Unarmed=Unarmed
 Overhaul.Name.Woodcutting=Woodcutting
+Overhaul.Name.Tridents=Tridents
+Overhaul.Name.Crossbows=Crossbows
 # /mcMMO Command Style Stuff
 Commands.mcc.Header=[[RED]]---[][[GREEN]]mcMMO Commands[[RED]][]---
 Commands.Other=[[RED]]---[][[GREEN]]SPECIAL COMMANDS[[RED]][]---
@@ -122,6 +124,8 @@ XPBar.Swords=Swords Lv.[[GOLD]]{0}
 XPBar.Taming=Taming Lv.[[GOLD]]{0}
 XPBar.Unarmed=Unarmed Lv.[[GOLD]]{0}
 XPBar.Woodcutting=Woodcutting Lv.[[GOLD]]{0}
+XPBar.Tridents=Tridents Lv.[[GOLD]]{0}
+XPBar.Crossbows=Crossbows Lv.[[GOLD]]{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
 XPBar.Complex.Template={0} [[DARK_AQUA]] {4}[[WHITE]]% [[DARK_AQUA]]([[WHITE]]{1}[[DARK_AQUA]]/[[WHITE]]{2}[[DARK_AQUA]])
 # 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

+ 44 - 0
src/main/resources/plugin.yml

@@ -146,6 +146,12 @@ commands:
     salvage:
         description: Detailed mcMMO skill info
         permission: mcmmo.commands.salvage
+    tridents:
+        description: Detailed mcMMO skill info
+        permission: mcmmo.commands.tridents
+    crossbows:
+        description: Detailed mcMMO skill info
+        permission: mcmmo.commands.crossbows
     adminchat:
         aliases: [ac, a]
         description: Toggle Admin chat or send admin chat messages
@@ -222,6 +228,8 @@ permissions:
             mcmmo.ability.taming.all: true
             mcmmo.ability.unarmed.all: true
             mcmmo.ability.woodcutting.all: true
+            mcmmo.ability.tridents.all: true
+            mcmmo.ability.crossbows.all: true
     mcmmo.ability.acrobatics.*:
         default: false
         description: Allows access to all Acrobatics abilities
@@ -709,6 +717,28 @@ permissions:
         description: Allows access to Leaf Blower ability
     mcmmo.ability.woodcutting.treefeller:
         description: Allows access to Tree Feller ability
+    mcmmo.ability.tridents.*:
+        default: false
+        description: Allows access to all Tridents abilities
+        children:
+            mcmmo.ability.tridents.all: true
+    mcmmo.ability.tridents.all:
+        description: Allows access to all Tridents abilities
+        children:
+            mcmmo.ability.tridents.multitasking: true
+    mcmmo.ability.tridents.multitasking:
+        description: Allows access to Multitasking Trident sub-skill
+    mcmmo.ability.crossbows.*:
+        default: false
+        description: Allows access to Crossbow sub-skills
+        children:
+            mcmmo.ability.crossbows.all: true
+    mcmmo.ability.crossbows.all:
+        description: Allows access to Crossbow sub-skills
+        children:
+            mcmmo.ability.crossbows.coneofdeath: true
+    mcmmo.ability.crossbows.coneofdeath:
+        description: Allows access to Cone of Death sub-skill
     mcmmo.admin:
         default: false
         description: Implies access to everything in mcMMO
@@ -795,6 +825,8 @@ permissions:
             mcmmo.commands.taming: true
             mcmmo.commands.unarmed: true
             mcmmo.commands.woodcutting: true
+            mcmmo.commands.tridents: true
+            mcmmo.commands.crossbows: true
     mcmmo.commands.defaultsop:
         description: Implies all default op mcmmo.commands permissions.
         children:
@@ -2007,6 +2039,8 @@ permissions:
             mcmmo.skills.taming: true
             mcmmo.skills.unarmed: true
             mcmmo.skills.woodcutting: true
+            mcmmo.skills.tridents: true
+            mcmmo.skills.crossbows: true
     mcmmo.skills.acrobatics:
         description: Allows access to the Acrobatics skill
         children:
@@ -2082,6 +2116,16 @@ permissions:
         children:
             mcmmo.ability.woodcutting.all: true
             mcmmo.commands.woodcutting: true
+    mcmmo.skills.tridents:
+        description: Allows access to the Tridents skill
+        children:
+            mcmmo.ability.tridents.all: true
+            mcmmo.commands.tridents: true
+    mcmmo.skills.crossbows:
+        description: Allows access to the Crossbows skill
+        children:
+            mcmmo.ability.crossbows.all: true
+            mcmmo.commands.crossbows: true
     mcmmo.showversion:
         default: true
         description: Show mcMMO version number in /mcmmo and motd

+ 34 - 0
src/main/resources/skillranks.yml

@@ -652,6 +652,40 @@ Repair:
         RetroMode:
             Rank_1: 400
     ArcaneForging:
+        Standard:
+            Rank_1: 10
+            Rank_2: 25
+            Rank_3: 35
+            Rank_4: 50
+            Rank_5: 65
+            Rank_6: 75
+            Rank_7: 85
+            Rank_8: 100
+        RetroMode:
+            Rank_1: 100
+            Rank_2: 250
+            Rank_3: 350
+            Rank_4: 500
+            Rank_5: 650
+            Rank_6: 750
+            Rank_7: 850
+            Rank_8: 1000
+Tridents:
+    MultiTasking:
+        Standard:
+            Rank_1: 5
+            Rank_2: 20
+            Rank_3: 30
+            Rank_4: 60
+            Rank_5: 100
+        RetroMode:
+            Rank_1: 50
+            Rank_2: 200
+            Rank_3: 300
+            Rank_4: 600
+            Rank_5: 1000
+Crossbows:
+    ConeOfDeath:
         Standard:
             Rank_1: 10
             Rank_2: 25

+ 2 - 0
src/main/resources/tridents.yml

@@ -0,0 +1,2 @@
+# In the Java version of Minecraft, Tridents are pretty rare to come by. Considering we have a whole skill around it, I added this setting to make it more viable.
+Craftable_Tridents: true