浏览代码

maces wip

nossr50 11 月之前
父节点
当前提交
e1dee2778d

+ 11 - 0
Changelog.txt

@@ -1,3 +1,14 @@
+# TODO: Add Maces to repair
+# TODO: Add subskills
+# TODO: Add Maces to combat utils
+# TODO: Skill ideas... Base DMG buff, Base Smash Buff, Momentum
+# TODO: Maces guide command
+# TODO: Maces Locale entries
+# TODO: Audit CombatUtils and make sure attack strength is being used
+Version 2.2.015
+    Added Maces skill
+    Fixed a bug where a player's attack cooldown was not being taken into account when adding bonus DMG
+
 Version 2.2.014
     Fixed a bug where Luck Of The Sea was being applied for Super Breaker (and other abilities)
 

+ 1 - 1
pom.xml

@@ -376,7 +376,7 @@
         <dependency>
             <groupId>org.spigotmc</groupId>
             <artifactId>spigot-api</artifactId>
-            <version>1.20.6-R0.1-SNAPSHOT</version>
+            <version>1.21-R0.1-SNAPSHOT</version>
             <scope>provided</scope>
         </dependency>
         <dependency>

+ 80 - 52
src/main/java/com/gmail/nossr50/commands/skills/MacesCommand.java

@@ -1,52 +1,80 @@
-//package com.gmail.nossr50.commands.skills;
-//
-//import com.gmail.nossr50.datatypes.player.McMMOPlayer;
-//import com.gmail.nossr50.datatypes.skills.PrimarySkillType;
-//import com.gmail.nossr50.util.player.UserManager;
-//import com.gmail.nossr50.util.skills.CombatUtils;
-//import com.gmail.nossr50.util.skills.SkillUtils;
-//import com.gmail.nossr50.util.text.TextComponentFactory;
-//import net.kyori.adventure.text.Component;
-//import org.bukkit.ChatColor;
-//import org.bukkit.entity.Player;
-//
-//import java.util.ArrayList;
-//import java.util.List;
-//
-//import static com.gmail.nossr50.datatypes.skills.SubSkillType.MACES_MACES_LIMIT_BREAK;
-//
-//public class MacesCommand extends SkillCommand {
-//
-//    public MacesCommand() {
-//        super(PrimarySkillType.MACES);
-//    }
-//
-//    @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) {
-//        List<String> messages = new ArrayList<>();
-//
-//        if (SkillUtils.canUseSubskill(player, MACES_MACES_LIMIT_BREAK)) {
-//            messages.add(getStatMessage(MACES_MACES_LIMIT_BREAK,
-//                    String.valueOf(CombatUtils.getLimitBreakDamageAgainstQuality(player, MACES_MACES_LIMIT_BREAK, 1000))));
-//        }
-//
-//        messages.add(ChatColor.GRAY + "The Maces skill is a work in progress and is still being developed," +
-//                " feedback would be appreciated in the mcMMO discord server.");
-//        return messages;
-//    }
-//
-//    @Override
-//    protected List<Component> getTextComponents(Player player) {
-//        List<Component> textComponents = new ArrayList<>();
-//
-//        TextComponentFactory.getSubSkillTextComponents(player, textComponents, PrimarySkillType.MACES);
-//
-//        return textComponents;
-//    }
-//}
+package com.gmail.nossr50.commands.skills;
+
+import com.gmail.nossr50.datatypes.skills.PrimarySkillType;
+import com.gmail.nossr50.datatypes.skills.SubSkillType;
+import com.gmail.nossr50.locale.LocaleLoader;
+import com.gmail.nossr50.mcMMO;
+import com.gmail.nossr50.util.skills.CombatUtils;
+import com.gmail.nossr50.util.skills.RankUtils;
+import com.gmail.nossr50.util.skills.SkillUtils;
+import com.gmail.nossr50.util.text.TextComponentFactory;
+import net.kyori.adventure.text.Component;
+import org.bukkit.ChatColor;
+import org.bukkit.entity.Player;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import static com.gmail.nossr50.datatypes.skills.SubSkillType.MACES_CRIPPLE;
+import static com.gmail.nossr50.datatypes.skills.SubSkillType.MACES_MACES_LIMIT_BREAK;
+
+public class MacesCommand extends SkillCommand {
+
+    public MacesCommand() {
+        super(PrimarySkillType.MACES);
+    }
+
+    String crippleChanceToApply, crippleChanceToApplyLucky, crippleLengthAgainstPlayers, crippleLengthAgainstMobs;
+
+    @Override
+    protected void dataCalculations(Player player, float skillValue) {
+        if (SkillUtils.canUseSubskill(player, MACES_CRIPPLE)) {
+            int crippleRank = RankUtils.getRank(player, MACES_CRIPPLE);
+            crippleLengthAgainstPlayers = String.valueOf("INSERT VALUE HERE");
+            crippleLengthAgainstMobs = String.valueOf("INSERT VALUE HERE");
+
+            crippleChanceToApply = String.valueOf(mcMMO.p.getAdvancedConfig().getCrippleChanceToApplyOnHit(crippleRank) + "%");
+            crippleChanceToApplyLucky = String.valueOf(mcMMO.p.getAdvancedConfig().getCrippleChanceToApplyOnHit(crippleRank) * 1.33);
+        }
+    }
+
+    @Override
+    protected void permissionsCheck(Player player) {}
+
+    @Override
+    protected List<String> statsDisplay(Player player, float skillValue, boolean hasEndurance, boolean isLucky) {
+        final List<String> messages = new ArrayList<>();
+
+        if (SkillUtils.canUseSubskill(player, MACES_MACES_LIMIT_BREAK)) {
+            messages.add(getStatMessage(MACES_MACES_LIMIT_BREAK,
+                    String.valueOf(CombatUtils.getLimitBreakDamageAgainstQuality(player,
+                            MACES_MACES_LIMIT_BREAK, 1000))));
+        }
+
+        if (SkillUtils.canUseSubskill(player, MACES_CRIPPLE)) {
+            messages.add(getStatMessage(MACES_CRIPPLE, crippleChanceToApply)
+                    + (isLucky ? LocaleLoader.getString("Perks.Lucky.Bonus", crippleChanceToApplyLucky) : ""));
+            messages.add(getStatMessage(true, true, MACES_CRIPPLE,
+                    crippleLengthAgainstPlayers,
+                    crippleLengthAgainstMobs));
+        }
+
+        if (SkillUtils.canUseSubskill(player, SubSkillType.MACES_CRUSH)) {
+            messages.add(getStatMessage(SubSkillType.MACES_CRUSH,
+                    String.valueOf(mmoPlayer.getMacesManager().getCrushDamage())));
+        }
+
+        messages.add(ChatColor.GRAY + "The Maces skill is a work in progress and is still being developed," +
+                " feedback would be appreciated in the mcMMO discord server.");
+        return messages;
+    }
+
+    @Override
+    protected List<Component> getTextComponents(Player player) {
+        List<Component> textComponents = new ArrayList<>();
+
+        TextComponentFactory.getSubSkillTextComponents(player, textComponents, PrimarySkillType.MACES);
+
+        return textComponents;
+    }
+}

+ 7 - 0
src/main/java/com/gmail/nossr50/config/AdvancedConfig.java

@@ -11,6 +11,7 @@ import java.util.ArrayList;
 import java.util.List;
 
 public class AdvancedConfig extends BukkitConfig {
+    int[] defaultCrippleValues = new int[]{10, 15, 20, 25};
 
     public AdvancedConfig(File dataFolder) {
         super("advanced.yml", dataFolder);
@@ -937,4 +938,10 @@ public class AdvancedConfig extends BukkitConfig {
     public boolean isKnockOnWoodXPOrbEnabled() {
         return config.getBoolean("Skills.Woodcutting.TreeFeller.Knock_On_Wood.Add_XP_Orbs_To_Drops", true);
     }
+
+    /* MACES */
+    public double getCrippleChanceToApplyOnHit(int rank) {
+        String root = "Skills.Maces.Cripple.Chance_To_Apply_On_Hit.Rank_";
+        return config.getDouble(root + rank, defaultCrippleValues[rank-1]);
+    }
 }

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

@@ -112,7 +112,6 @@ public class McMMOPlayer implements Identified {
     private int respawnATS;
     private int teleportATS;
     private long databaseATS;
-    private double attackStrength; //captured during arm swing events
     //private int chimeraWingLastUse;
     private Location teleportCommence;
 
@@ -151,7 +150,6 @@ public class McMMOPlayer implements Identified {
         experienceBarManager = new ExperienceBarManager(this);
 
         debugMode = false; //Debug mode helps solve support issues, players can toggle it on or off
-        attackStrength = 1.0D;
 
         this.playerAuthor = new PlayerAuthor(player);
 
@@ -239,7 +237,7 @@ public class McMMOPlayer implements Identified {
     }
 
     public double getAttackStrength() {
-        return attackStrength;
+        return player.getAttackCooldown();
     }
 
     public @NotNull PrimarySkillType getLastSkillShownScoreboard() {

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

@@ -59,6 +59,8 @@ public enum SubSkillType {
 
     /* Maces */
     MACES_MACES_LIMIT_BREAK(10),
+    MACES_CRUSH(4),
+    MACES_CRIPPLE(4),
 
     /* Mining */
     MINING_BIGGER_BOMBS(1),

+ 63 - 0
src/main/java/com/gmail/nossr50/skills/maces/MacesManager.java

@@ -2,10 +2,73 @@ package com.gmail.nossr50.skills.maces;
 
 import com.gmail.nossr50.datatypes.player.McMMOPlayer;
 import com.gmail.nossr50.datatypes.skills.PrimarySkillType;
+import com.gmail.nossr50.datatypes.skills.SubSkillType;
+import com.gmail.nossr50.mcMMO;
 import com.gmail.nossr50.skills.SkillManager;
+import com.gmail.nossr50.util.Permissions;
+import com.gmail.nossr50.util.player.NotificationManager;
+import com.gmail.nossr50.util.random.ProbabilityUtil;
+import com.gmail.nossr50.util.skills.RankUtils;
+import com.gmail.nossr50.util.sounds.SoundManager;
+import com.gmail.nossr50.util.sounds.SoundType;
+import org.bukkit.SoundCategory;
+import org.bukkit.entity.LivingEntity;
+import org.bukkit.potion.PotionEffect;
+import org.bukkit.potion.PotionEffectType;
+import org.jetbrains.annotations.NotNull;
 
 public class MacesManager extends SkillManager {
     public MacesManager(McMMOPlayer mmoPlayer) {
         super(mmoPlayer, PrimarySkillType.MACES);
     }
+
+    /**
+     * Get the Crush damage bonus.
+     *
+     * @return the Crush damage bonus.
+     */
+    public double getCrushDamage() {
+        if (!Permissions.canUseSubSkill(mmoPlayer.getPlayer(), SubSkillType.MACES_CRUSH))
+            return 0;
+
+        int rank = RankUtils.getRank(getPlayer(), SubSkillType.MACES_CRUSH);
+
+        if (rank > 0) {
+            return (1.0D + (rank * 0.5D));
+        }
+
+        return 0;
+    }
+
+    /**
+     * Process Cripple attack.
+     *
+     * @param target The defending entity
+     */
+    public void processCripple(@NotNull LivingEntity target) {
+        // Don't apply Cripple if the target is already Slowed
+        if (target.getPotionEffect(PotionEffectType.SLOWNESS) != null) {
+            return;
+        }
+
+        if (!Permissions.canUseSubSkill(mmoPlayer.getPlayer(), SubSkillType.MACES_CRIPPLE)) {
+            return;
+        }
+
+        int crippleRank = RankUtils.getRank(getPlayer(), SubSkillType.MACES_CRIPPLE);
+        double crippleOdds = (mcMMO.p.getAdvancedConfig().getCrippleChanceToApplyOnHit(crippleRank)
+                * mmoPlayer.getAttackStrength());
+
+        if (ProbabilityUtil.isStaticSkillRNGSuccessful(PrimarySkillType.MACES, mmoPlayer, crippleOdds)) {
+            // Cripple is success, Cripple the target
+            target.addPotionEffect(PotionEffectType.SLOWNESS.createEffect(getCrippleTickDuration(), 1));
+            // TODO: Play some kind of Smash effect / sound
+            SoundManager.sendCategorizedSound(getPlayer(), target.getLocation(), SoundType.CRIPPLE, SoundCategory.PLAYERS);
+        }
+    }
+
+    public int getCrippleTickDuration() {
+        // TODO: Make configurable
+        return 20 * 5;
+    }
 }

+ 31 - 79
src/main/java/com/gmail/nossr50/util/commands/CommandRegistrationManager.java

@@ -32,95 +32,47 @@ public final class CommandRegistrationManager {
     private static final String permissionsMessage = LocaleLoader.getString("mcMMO.NoPermission");
 
     private static void registerSkillCommands() {
-        for (PrimarySkillType skill : PrimarySkillType.values()) {
-            if (skill == PrimarySkillType.MACES)
+        for (PrimarySkillType primarySkillType : PrimarySkillType.values()) {
+            if (primarySkillType == PrimarySkillType.MACES
+                    && !mcMMO.getCompatibilityManager().getMinecraftGameVersion().isAtLeast(1, 21, 0)) {
                 continue;
+            }
 
-            String commandName = skill.toString().toLowerCase(Locale.ENGLISH);
-            String localizedName = mcMMO.p.getSkillTools().getLocalizedSkillName(skill).toLowerCase(Locale.ENGLISH);
+            final String commandName = primarySkillType.toString().toLowerCase(Locale.ENGLISH);
+            final String localizedName = mcMMO.p.getSkillTools().getLocalizedSkillName(primarySkillType).toLowerCase(Locale.ENGLISH);
 
-            PluginCommand command;
+            final PluginCommand command = mcMMO.p.getCommand(commandName);
+            if (command == null) {
+                mcMMO.p.getLogger().severe("Command not found: " + commandName);
+                continue;
+            }
 
-            command = mcMMO.p.getCommand(commandName);
             command.setDescription(LocaleLoader.getString("Commands.Description.Skill", StringUtils.getCapitalized(localizedName)));
             command.setPermission("mcmmo.commands." + commandName);
             command.setPermissionMessage(permissionsMessage);
             command.setUsage(LocaleLoader.getString("Commands.Usage.0", commandName));
             command.setUsage(command.getUsage() + "\n" + LocaleLoader.getString("Commands.Usage.2", commandName, "?", "[" + LocaleLoader.getString("Commands.Usage.Page") + "]"));
 
-            switch (skill) {
-                case ACROBATICS:
-                    command.setExecutor(new AcrobaticsCommand());
-                    break;
-
-                case ALCHEMY:
-                    command.setExecutor(new AlchemyCommand());
-                    break;
-
-                case ARCHERY:
-                    command.setExecutor(new ArcheryCommand());
-                    break;
-
-                case AXES:
-                    command.setExecutor(new AxesCommand());
-                    break;
-                case CROSSBOWS:
-                    command.setExecutor(new CrossbowsCommand());
-                    break;
-
-                case EXCAVATION:
-                    command.setExecutor(new ExcavationCommand());
-                    break;
-
-                case FISHING:
-                    command.setExecutor(new FishingCommand());
-                    break;
-
-                case HERBALISM:
-                    command.setExecutor(new HerbalismCommand());
-                    break;
-
-                case MACES:
-                    // command.setExecutor(new MacesCommand());
-                    break;
-
-                case MINING:
-                    command.setExecutor(new MiningCommand());
-                    break;
-
-                case REPAIR:
-                    command.setExecutor(new RepairCommand());
-                    break;
-
-                case SALVAGE:
-                    command.setExecutor(new SalvageCommand());
-                    break;
-
-                case SMELTING:
-                    command.setExecutor(new SmeltingCommand());
-                    break;
-
-                case SWORDS:
-                    command.setExecutor(new SwordsCommand());
-                    break;
-
-                case TAMING:
-                    command.setExecutor(new TamingCommand());
-                    break;
-                case TRIDENTS:
-                    command.setExecutor(new TridentsCommand());
-                    break;
-
-                case UNARMED:
-                    command.setExecutor(new UnarmedCommand());
-                    break;
-
-                case WOODCUTTING:
-                    command.setExecutor(new WoodcuttingCommand());
-                    break;
-
-                default:
-                    throw new IllegalStateException("Unexpected value: " + skill);
+            switch (primarySkillType) {
+                case ACROBATICS -> command.setExecutor(new AcrobaticsCommand());
+                case ALCHEMY -> command.setExecutor(new AlchemyCommand());
+                case ARCHERY -> command.setExecutor(new ArcheryCommand());
+                case AXES -> command.setExecutor(new AxesCommand());
+                case CROSSBOWS -> command.setExecutor(new CrossbowsCommand());
+                case EXCAVATION -> command.setExecutor(new ExcavationCommand());
+                case FISHING -> command.setExecutor(new FishingCommand());
+                case HERBALISM -> command.setExecutor(new HerbalismCommand());
+                case MACES -> command.setExecutor(new MacesCommand());
+                case MINING -> command.setExecutor(new MiningCommand());
+                case REPAIR -> command.setExecutor(new RepairCommand());
+                case SALVAGE -> command.setExecutor(new SalvageCommand());
+                case SMELTING -> command.setExecutor(new SmeltingCommand());
+                case SWORDS -> command.setExecutor(new SwordsCommand());
+                case TAMING -> command.setExecutor(new TamingCommand());
+                case TRIDENTS -> command.setExecutor(new TridentsCommand());
+                case UNARMED -> command.setExecutor(new UnarmedCommand());
+                case WOODCUTTING -> command.setExecutor(new WoodcuttingCommand());
+                default -> throw new IllegalStateException("Unexpected value: " + primarySkillType);
             }
         }
     }

+ 5 - 8
src/main/java/com/gmail/nossr50/util/platform/PlatformManager.java

@@ -85,14 +85,11 @@ public class PlatformManager {
     }
 
     public String getServerSoftwareStr() {
-        switch(getServerSoftware()) {
-            case PAPER:
-                return "Paper";
-            case SPIGOT:
-                return "Spigot";
-            default:
-                return "CraftBukkit";
-        }
+        return switch (getServerSoftware()) {
+            case PAPER -> "Paper";
+            case SPIGOT -> "Spigot";
+            default -> "CraftBukkit";
+        };
     }
 
     public @Nullable CompatibilityManager getCompatibilityManager() {

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

@@ -13,6 +13,7 @@ import com.gmail.nossr50.runnables.skills.AwardCombatXpTask;
 import com.gmail.nossr50.skills.acrobatics.AcrobaticsManager;
 import com.gmail.nossr50.skills.archery.ArcheryManager;
 import com.gmail.nossr50.skills.axes.AxesManager;
+import com.gmail.nossr50.skills.maces.MacesManager;
 import com.gmail.nossr50.skills.swords.SwordsManager;
 import com.gmail.nossr50.skills.taming.TamingManager;
 import com.gmail.nossr50.skills.tridents.TridentsManager;
@@ -20,6 +21,7 @@ import com.gmail.nossr50.skills.unarmed.UnarmedManager;
 import com.gmail.nossr50.util.*;
 import com.gmail.nossr50.util.player.NotificationManager;
 import com.gmail.nossr50.util.player.UserManager;
+import org.bukkit.Bukkit;
 import org.bukkit.GameMode;
 import org.bukkit.Material;
 import org.bukkit.attribute.Attribute;
@@ -72,10 +74,6 @@ public final class CombatUtils {
             mcMMOPlayer.checkAbilityActivation(PrimarySkillType.SWORDS);
         }
 
-        if (target.getHealth() - event.getFinalDamage() > 0) {
-            swordsManager.processRupture(target);
-        }
-
         //Add Stab Damage
         if (swordsManager.canUseStab()) {
             boostedDamage += (swordsManager.getStabDamage() * mcMMOPlayer.getAttackStrength());
@@ -90,6 +88,11 @@ public final class CombatUtils {
         }
 
         event.setDamage(boostedDamage);
+
+        if (target.getHealth() - event.getFinalDamage() > 0) {
+            swordsManager.processRupture(target);
+        }
+
         processCombatXP(mcMMOPlayer, target, PrimarySkillType.SWORDS);
 
         printFinalDamageDebug(player, event, mcMMOPlayer);
@@ -210,29 +213,40 @@ public final class CombatUtils {
         delayArrowMetaCleanup(arrow);
     }
 
-    private static void processMacesCombat(@NotNull LivingEntity target, @NotNull Player player, @NotNull EntityDamageByEntityEvent event) {
+    private static void processMacesCombat(@NotNull LivingEntity target,
+                                           @NotNull Player player,
+                                           @NotNull EntityDamageByEntityEvent event) {
         if (event.getCause() == DamageCause.THORNS) {
             return;
         }
 
         double boostedDamage = event.getDamage();
 
-        McMMOPlayer mcMMOPlayer = UserManager.getPlayer(player);
+        final McMMOPlayer mcMMOPlayer = UserManager.getPlayer(player);
 
         //Make sure the profiles been loaded
         if (mcMMOPlayer == null) {
             return;
         }
 
-        // MacesManager macesManager = mcMMOPlayer.getMacesManager();
+        final MacesManager macesManager = mcMMOPlayer.getMacesManager();
 
+        // Apply Limit Break DMG
         if (canUseLimitBreak(player, target, SubSkillType.MACES_MACES_LIMIT_BREAK)) {
             boostedDamage += (getLimitBreakDamage(player, target, SubSkillType.MACES_MACES_LIMIT_BREAK) * mcMMOPlayer.getAttackStrength());
         }
 
+        // Apply Crush
+        boostedDamage += (macesManager.getCrushDamage() * mcMMOPlayer.getAttackStrength());
+
         event.setDamage(boostedDamage);
-        processCombatXP(mcMMOPlayer, target, PrimarySkillType.MACES);
 
+        // Apply Cripple
+        if (target.getHealth() - event.getFinalDamage() > 0) {
+            macesManager.processCripple(target);
+        }
+
+        processCombatXP(mcMMOPlayer, target, PrimarySkillType.MACES);
         printFinalDamageDebug(player, event, mcMMOPlayer);
     }
 
@@ -416,7 +430,9 @@ public final class CombatUtils {
      *
      * @param event The event to run the combat checks on.
      */
-    public static void processCombatAttack(@NotNull EntityDamageByEntityEvent event, @NotNull Entity painSourceRoot, @NotNull LivingEntity target) {
+    public static void processCombatAttack(@NotNull EntityDamageByEntityEvent event,
+                                           @NotNull Entity painSourceRoot,
+                                           @NotNull LivingEntity target) {
         Entity painSource = event.getDamager();
         EntityType entityType = painSource.getType();
 
@@ -456,6 +472,11 @@ public final class CombatUtils {
         }
 
         if (painSourceRoot instanceof Player player && entityType == EntityType.PLAYER) {
+//            final McMMOPlayer mcMMOPlayer = UserManager.getPlayer(player);
+//            if (mcMMOPlayer != null) {
+//                Bukkit.broadcastMessage("DEBUG: AttackStrength of painSource: " + mcMMOPlayer.getAttackStrength());
+//                System.out.println("DEBUG: AttackStrength of painSource: " + mcMMOPlayer.getAttackStrength());
+//            }
 
             if (!UserManager.hasPlayerDataKey(player)) {
                 return;

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

@@ -9,6 +9,15 @@ import org.bukkit.World;
 import org.bukkit.entity.Player;
 
 public class SoundManager {
+    public static Sound CRIPPLE_SOUND;
+    static {
+        try {
+            CRIPPLE_SOUND = Sound.valueOf("ITEM_MACE_SMASH_GROUND");
+        } catch (IllegalArgumentException e) {
+            CRIPPLE_SOUND = Sound.BLOCK_ANVIL_PLACE;
+        }
+    }
+
     /**
      * Sends a sound to the player
      * @param soundType the type of sound
@@ -74,6 +83,7 @@ public class SoundManager {
             case DEFLECT_ARROWS, BLEED -> Sound.ENTITY_ENDER_EYE_DEATH;
             case GLASS -> Sound.BLOCK_GLASS_BREAK;
             case ITEM_CONSUMED -> Sound.ITEM_BOTTLE_EMPTY;
+            case CRIPPLE -> CRIPPLE_SOUND;
         };
     }
 

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

@@ -16,6 +16,7 @@ public enum SoundType {
     BLEED,
     GLASS,
     ITEM_CONSUMED,
+    CRIPPLE,
     TIRED;
 
     public boolean usesCustomPitch() {

+ 8 - 1
src/main/resources/advanced.yml

@@ -633,4 +633,11 @@ Skills:
             ChanceMax: 100.0
             MaxBonusLevel:
                 Standard: 100
-                RetroMode: 1000
+                RetroMode: 1000
+    Maces:
+        Cripple:
+            Chance_To_Apply_On_Hit:
+                Rank_1: 10
+                Rank_2: 15
+                Rank_3: 20
+                Rank_4: 33

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

@@ -464,6 +464,13 @@ Maces.Ability.Ready=&3You &6ready&3 your Mace.
 Maces.SubSkill.MacesLimitBreak.Name=Maces Limit Break
 Maces.SubSkill.MacesLimitBreak.Description=Breaking your limits. Increased damage against tough opponents. Intended for PVP, up to server settings for whether it will boost damage in PVE.
 Maces.SubSkill.MacesLimitBreak.Stat=Limit Break Max DMG
+Maces.SubSkill.Crush.Name=Crush
+Maces.SubSkill.Crush.Description=Adds bonus damage to your attacks.
+Maces.SubSkill.Crush.Stat=Crush Damage
+Maces.SubSkill.Cripple.Name=Cripple
+Maces.SubSkill.Cripple.Description=Adds a chance to cripple your target.
+Maces.SubSkill.Cripple.Stat=Cripple Chance
+Maces.SubSkill.Cripple.Stat.Extra=[[DARK_AQUA]]Cripple Duration: &e{0}s&a vs Players, &e{1}s&a vs Mobs.
 Maces.Listener=Maces:
 
 #SWORDS
@@ -899,6 +906,7 @@ Commands.XPGain.Crossbows=Attacking Monsters
 Commands.XPGain.Excavation=Digging and finding treasures
 Commands.XPGain.Fishing=Fishing (Go figure!)
 Commands.XPGain.Herbalism=Harvesting Herbs
+Commands.XPGain.Maces=Attacking Monsters
 Commands.XPGain.Mining=Mining Stone & Ore
 Commands.XPGain.Repair=Repairing
 Commands.XPGain.Swords=Attacking Monsters

+ 45 - 22
src/main/resources/skillranks.yml

@@ -444,28 +444,51 @@ Salvage:
             Rank_7: 850
             Rank_8: 1000
 Maces:
-    Standard:
-        Rank_1: 10
-        Rank_2: 20
-        Rank_3: 30
-        Rank_4: 40
-        Rank_5: 50
-        Rank_6: 60
-        Rank_7: 70
-        Rank_8: 80
-        Rank_9: 90
-        Rank_10: 100
-    RetroMode:
-        Rank_1: 100
-        Rank_2: 200
-        Rank_3: 300
-        Rank_4: 400
-        Rank_5: 500
-        Rank_6: 600
-        Rank_7: 700
-        Rank_8: 800
-        Rank_9: 900
-        Rank_10: 1000
+    MacesLimitBreak:
+        Standard:
+            Rank_1: 10
+            Rank_2: 20
+            Rank_3: 30
+            Rank_4: 40
+            Rank_5: 50
+            Rank_6: 60
+            Rank_7: 70
+            Rank_8: 80
+            Rank_9: 90
+            Rank_10: 100
+        RetroMode:
+            Rank_1: 100
+            Rank_2: 200
+            Rank_3: 300
+            Rank_4: 400
+            Rank_5: 500
+            Rank_6: 600
+            Rank_7: 700
+            Rank_8: 800
+            Rank_9: 900
+            Rank_10: 1000
+    Cripple:
+        Standard:
+            Rank_1: 5
+            Rank_2: 20
+            Rank_3: 40
+            Rank_4: 80
+        RetroMode:
+            Rank_1: 50
+            Rank_2: 200
+            Rank_3: 400
+            Rank_4: 800
+    Crush:
+        Standard:
+            Rank_1: 10
+            Rank_2: 25
+            Rank_3: 75
+            Rank_4: 90
+        RetroMode:
+            Rank_1: 100
+            Rank_2: 250
+            Rank_3: 750
+            Rank_4: 900
 Mining:
     MotherLode:
         Standard:

+ 0 - 2
src/test/java/com/gmail/nossr50/skills/woodcutting/WoodcuttingTest.java

@@ -17,9 +17,7 @@ import org.junit.jupiter.api.BeforeEach;
 import org.junit.jupiter.api.Test;
 import org.mockito.Mockito;
 
-import java.util.Collection;
 import java.util.Collections;
-import java.util.List;
 
 import static org.mockito.ArgumentMatchers.any;
 import static org.mockito.ArgumentMatchers.eq;