Explorar o código

Crossbow Fixes

nossr50 hai 1 ano
pai
achega
b3b8a12b6d

+ 1 - 1
pom.xml

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

+ 2 - 3
src/main/java/com/gmail/nossr50/commands/skills/TridentsCommand.java

@@ -2,20 +2,19 @@ package com.gmail.nossr50.commands.skills;
 
 import com.gmail.nossr50.datatypes.player.McMMOPlayer;
 import com.gmail.nossr50.datatypes.skills.PrimarySkillType;
-import com.gmail.nossr50.datatypes.skills.SubSkillType;
 import com.gmail.nossr50.util.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.Cat;
 import org.bukkit.entity.Player;
 
 import java.util.ArrayList;
 import java.util.List;
 
-import static com.gmail.nossr50.datatypes.skills.SubSkillType.*;
+import static com.gmail.nossr50.datatypes.skills.SubSkillType.TRIDENTS_IMPALE;
+import static com.gmail.nossr50.datatypes.skills.SubSkillType.TRIDENTS_TRIDENTS_LIMIT_BREAK;
 
 public class TridentsCommand extends SkillCommand {
 

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

@@ -963,14 +963,14 @@ public class McMMOPlayer implements Identified {
     /**
      * Check to see if an ability can be activated.
      *
-     * @param bowType The type of bow (crossbow, bow)
+     * @param isCrossbow true for crossbow, false for bow
      */
-    public void checkAbilityActivationProjectiles(BowType bowType) {
-        PrimarySkillType primarySkillType = bowType == BowType.CROSSBOW ? PrimarySkillType.CROSSBOWS : PrimarySkillType.ARCHERY;
+    public void checkAbilityActivationProjectiles(boolean isCrossbow) {
+        PrimarySkillType primarySkillType = isCrossbow ? PrimarySkillType.CROSSBOWS : PrimarySkillType.ARCHERY;
 
         // TODO: Refactor this crappy logic
-        ToolType tool = bowType == BowType.CROSSBOW ? ToolType.CROSSBOW : ToolType.BOW;
-        SuperAbilityType superAbilityType = bowType == BowType.CROSSBOW ? SuperAbilityType.SUPER_SHOTGUN : SuperAbilityType.EXPLOSIVE_SHOT;
+        ToolType tool = isCrossbow ? ToolType.CROSSBOW : ToolType.BOW;
+        SuperAbilityType superAbilityType = isCrossbow ? SuperAbilityType.SUPER_SHOTGUN : SuperAbilityType.EXPLOSIVE_SHOT;
         SubSkillType subSkillType = superAbilityType.getSubSkillTypeDefinition();
 
         if (getAbilityMode(superAbilityType) || !superAbilityType.getPermissions(player)) {

+ 14 - 23
src/main/java/com/gmail/nossr50/listeners/EntityListener.java

@@ -26,7 +26,6 @@ import com.gmail.nossr50.util.player.NotificationManager;
 import com.gmail.nossr50.util.player.UserManager;
 import com.gmail.nossr50.util.random.ProbabilityUtil;
 import com.gmail.nossr50.util.skills.CombatUtils;
-import com.gmail.nossr50.util.skills.ProjectileUtils;
 import com.gmail.nossr50.worldguard.WorldGuardManager;
 import com.gmail.nossr50.worldguard.WorldGuardUtils;
 import org.bukkit.ChatColor;
@@ -105,7 +104,7 @@ public class EntityListener implements Listener {
         }
     }
 
-    @EventHandler(priority = EventPriority.MONITOR, ignoreCancelled = true)
+    @EventHandler(priority = EventPriority.MONITOR, ignoreCancelled = false)
     public void onEntityShootBow(EntityShootBowEvent event) {
         /* WORLD BLACKLIST CHECK */
         if(WorldBlacklist.isWorldBlacklisted(event.getEntity().getWorld()))
@@ -113,18 +112,10 @@ public class EntityListener implements Listener {
 
         if(event.getEntity() instanceof Player player)
         {
-
-            /* WORLD GUARD MAIN FLAG CHECK */
-            if(WorldGuardUtils.isWorldGuardLoaded())
-            {
-                if(!WorldGuardManager.getInstance().hasMainFlag(player))
-                    return;
-            }
-
             Entity projectile = event.getProjectile();
 
             //Should be noted that there are API changes regarding Arrow from 1.13.2 to current versions of the game
-            if (!(projectile instanceof Arrow)) {
+            if (!(projectile instanceof Arrow arrow)) {
                 return;
             }
 
@@ -133,20 +124,16 @@ public class EntityListener implements Listener {
             if (bow == null)
                 return;
 
-            // determine if bow or crossbow
-            BowType bowType = ItemUtils.isCrossbow(bow) ? BowType.CROSSBOW : BowType.BOW;
-
             if (bow.containsEnchantment(Enchantment.ARROW_INFINITE)) {
                 projectile.setMetadata(MetadataConstants.METADATA_KEY_INF_ARROW, MetadataConstants.MCMMO_METADATA_VALUE);
             }
 
             // Set BowType, Force, and Distance metadata
-            projectile.setMetadata(MetadataConstants.METADATA_KEY_BOW_TYPE, new FixedMetadataValue(pluginRef, bowType));
             projectile.setMetadata(MetadataConstants.METADATA_KEY_BOW_FORCE, new FixedMetadataValue(pluginRef, Math.min(event.getForce() * mcMMO.p.getAdvancedConfig().getForceMultiplier(), 1.0)));
-            projectile.setMetadata(MetadataConstants.METADATA_KEY_ARROW_DISTANCE, new FixedMetadataValue(pluginRef, projectile.getLocation()));
+            projectile.setMetadata(MetadataConstants.METADATA_KEY_ARROW_DISTANCE, new FixedMetadataValue(pluginRef, arrow.getLocation()));
 
             //Cleanup metadata in 1 minute in case normal collection falls through
-            CombatUtils.delayArrowMetaCleanup((Projectile) projectile);
+            CombatUtils.delayArrowMetaCleanup(arrow);
         }
     }
 
@@ -168,14 +155,14 @@ public class EntityListener implements Listener {
             Projectile projectile = event.getEntity();
             EntityType entityType = projectile.getType();
 
-            if(entityType == EntityType.ARROW || entityType == EntityType.SPECTRAL_ARROW) {
-                CombatUtils.delayArrowMetaCleanup(projectile); //Cleans up metadata 1 minute from now in case other collection methods fall through
+            if(projectile instanceof Arrow arrow) {
+                CombatUtils.delayArrowMetaCleanup(arrow); //Cleans up metadata 1 minute from now in case other collection methods fall through
 
                 if(!projectile.hasMetadata(MetadataConstants.METADATA_KEY_BOW_FORCE))
                     projectile.setMetadata(MetadataConstants.METADATA_KEY_BOW_FORCE, new FixedMetadataValue(pluginRef, 1.0));
 
                 if(!projectile.hasMetadata(MetadataConstants.METADATA_KEY_ARROW_DISTANCE))
-                    projectile.setMetadata(MetadataConstants.METADATA_KEY_ARROW_DISTANCE, new FixedMetadataValue(pluginRef, projectile.getLocation()));
+                    projectile.setMetadata(MetadataConstants.METADATA_KEY_ARROW_DISTANCE, new FixedMetadataValue(pluginRef, arrow.getLocation()));
 
                 //Check both hands
                 if(ItemUtils.doesPlayerHaveEnchantmentInHands(player, "piercing")) {
@@ -411,8 +398,8 @@ public class EntityListener implements Listener {
             }
         }
 
-        if(entityDamageEvent.getDamager() instanceof Projectile) {
-            ProjectileUtils.cleanupProjectileMetadata((Projectile) entityDamageEvent.getDamager());
+        if(entityDamageEvent.getDamager() instanceof Arrow arrow) {
+            CombatUtils.delayArrowMetaCleanup(arrow);
         }
 
         if(entityDamageEvent.getEntity() instanceof Player player && entityDamageEvent.getDamager() instanceof Player) {
@@ -1119,6 +1106,10 @@ public class EntityListener implements Listener {
         if (WorldBlacklist.isWorldBlacklisted(event.getEntity().getWorld()))
             return;
 
-        Crossbows.processCrossbows(event, pluginRef);
+        if(event.getEntity() instanceof Arrow arrow) {
+            if(arrow.isShotFromCrossbow()) {
+                Crossbows.processCrossbows(event, pluginRef, arrow);
+            }
+        }
     }
 }

+ 5 - 5
src/main/java/com/gmail/nossr50/skills/crossbows/Crossbows.java

@@ -13,14 +13,14 @@ import static com.gmail.nossr50.util.skills.ProjectileUtils.getNormal;
  * Util class for crossbows.
  */
 public class Crossbows {
-    public static void processCrossbows(ProjectileHitEvent event, Plugin pluginRef) {
-        if(event.getEntity() instanceof Arrow originalArrow && event.getHitBlock() != null && event.getHitBlockFace() != null) {
-            if (originalArrow.getShooter() instanceof Player) {
-                McMMOPlayer mmoPlayer = UserManager.getPlayer((Player) originalArrow.getShooter());
+    public static void processCrossbows(ProjectileHitEvent event, Plugin pluginRef, Arrow arrow) {
+        if(event.getHitBlock() != null && event.getHitBlockFace() != null) {
+            if (arrow.getShooter() instanceof Player) {
+                McMMOPlayer mmoPlayer = UserManager.getPlayer((Player) arrow.getShooter());
                 if (mmoPlayer != null) {
                     mmoPlayer.getCrossbowsManager().handleRicochet(
                             pluginRef,
-                            originalArrow,
+                            arrow,
                             getNormal(event.getHitBlockFace()));
                 }
             }

+ 7 - 8
src/main/java/com/gmail/nossr50/skills/crossbows/CrossbowsManager.java

@@ -5,8 +5,6 @@ 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.skills.archery.Archery;
-import com.gmail.nossr50.util.BowType;
 import com.gmail.nossr50.util.MetadataConstants;
 import com.gmail.nossr50.util.Permissions;
 import com.gmail.nossr50.util.random.ProbabilityUtil;
@@ -25,17 +23,20 @@ public class CrossbowsManager extends SkillManager {
         super(mmoPlayer, PrimarySkillType.CROSSBOWS);
     }
 
-    public void handleRicochet(@NotNull Plugin pluginRef, @NotNull Arrow originalArrow, @NotNull Vector hitBlockNormal) {
+    public void handleRicochet(@NotNull Plugin pluginRef, @NotNull Arrow arrow, @NotNull Vector hitBlockNormal) {
+        if(!arrow.isShotFromCrossbow())
+            return;
+
         // Check player permission
         if (!Permissions.trickShot(mmoPlayer.getPlayer())) {
             return;
         }
 
         // TODO: Add an event for this for plugins to hook into
-        spawnReflectedArrow(pluginRef, originalArrow, originalArrow.getLocation(), hitBlockNormal);
+        spawnReflectedArrow(pluginRef, arrow, arrow.getLocation(), hitBlockNormal);
     }
 
-    public void spawnReflectedArrow(@NotNull Plugin pluginRef, @NotNull Arrow originalArrow,
+    private void spawnReflectedArrow(@NotNull Plugin pluginRef, @NotNull Arrow originalArrow,
                                     @NotNull Location origin, @NotNull Vector normal) {
         int bounceCount = 0;
 
@@ -66,8 +67,6 @@ public class CrossbowsManager extends SkillManager {
                 new FixedMetadataValue(pluginRef, bounceCount + 1));
         arrow.setMetadata(MetadataConstants.METADATA_KEY_SPAWNED_ARROW,
                 new FixedMetadataValue(pluginRef, originalArrowShooter));
-        arrow.setMetadata(MetadataConstants.METADATA_KEY_BOW_TYPE,
-                new FixedMetadataValue(pluginRef, BowType.CROSSBOW));
 
         originalArrow.remove();
     }
@@ -89,7 +88,7 @@ public class CrossbowsManager extends SkillManager {
 
     public double poweredShot(double oldDamage) {
         if (ProbabilityUtil.isNonRNGSkillActivationSuccessful(SubSkillType.CROSSBOWS_POWERED_SHOT, getPlayer())) {
-            return Archery.getSkillShotBonusDamage(getPlayer(), oldDamage);
+            return getPoweredShotBonusDamage(getPlayer(), oldDamage);
         } else {
             return oldDamage;
         }

+ 7 - 3
src/main/java/com/gmail/nossr50/skills/excavation/ExcavationManager.java

@@ -35,6 +35,7 @@ public class ExcavationManager extends SkillManager {
      * @param blockState The {@link BlockState} to check ability activation for
      */
     public void excavationBlockCheck(BlockState blockState) {
+        int xp = Excavation.getBlockXP(blockState);
         requireNonNull(blockState, "excavationBlockCheck: blockState cannot be null");
         if (Permissions.isSubSkillEnabled(getPlayer(), SubSkillType.EXCAVATION_ARCHAEOLOGY)) {
             List<ExcavationTreasure> treasures = getTreasures(blockState);
@@ -51,6 +52,8 @@ public class ExcavationManager extends SkillManager {
                 }
             }
         }
+
+        applyXpGain(xp, XPGainReason.PVE, XPGainSource.SELF);
     }
 
     @VisibleForTesting
@@ -61,16 +64,17 @@ public class ExcavationManager extends SkillManager {
 
     @VisibleForTesting
     public void processExcavationBonusesOnBlock(BlockState blockState, ExcavationTreasure treasure, Location location) {
-        int xp = Excavation.getBlockXP(blockState);
-
         //Spawn Vanilla XP orbs if a dice roll succeeds
         if(ProbabilityUtil.isStaticSkillRNGSuccessful(PrimarySkillType.EXCAVATION, getPlayer(), getArchaelogyExperienceOrbChance())) {
             Misc.spawnExperienceOrb(location, getExperienceOrbsReward());
         }
 
+        int xp = 0;
         xp += treasure.getXp();
         Misc.spawnItem(getPlayer(), location, treasure.getDrop(), ItemSpawnReason.EXCAVATION_TREASURE);
-        applyXpGain(xp, XPGainReason.PVE, XPGainSource.SELF);
+        if (xp > 0) {
+            applyXpGain(xp, XPGainReason.PVE, XPGainSource.SELF);
+        }
     }
 
     public int getExperienceOrbsReward() {

+ 0 - 6
src/main/java/com/gmail/nossr50/util/BowType.java

@@ -1,6 +0,0 @@
-package com.gmail.nossr50.util;
-
-public enum BowType {
-    BOW,
-    CROSSBOW
-}

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

@@ -51,17 +51,6 @@ public final class ItemUtils {
         return isBow(item) || isCrossbow(item);
     }
 
-    // TODO: Unit tests
-    public static BowType getBowType(@NotNull ItemStack item) {
-        if (isBow(item)) {
-            return BowType.BOW;
-        } else if (isCrossbow(item)) {
-            return BowType.CROSSBOW;
-        }
-
-        throw new IllegalArgumentException(item + " is not a bow or crossbow");
-    }
-
     // TODO: Unit tests
     public static boolean isTrident(@NotNull ItemStack item) {
         return mcMMO.getMaterialMapStore().isTrident(item.getType().getKey().getKey());

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

@@ -16,7 +16,6 @@ public class MetadataConstants {
     public static final @NotNull String METADATA_KEY_REPLANT = "mcMMO: Recently Replanted";
     public static final @NotNull String METADATA_KEY_SPAWNED_ARROW = "mcMMO: Spawned Arrow";
     public static final @NotNull String METADATA_KEY_BOUNCE_COUNT = "mcMMO: Arrow Bounce Count";
-    public static final @NotNull String METADATA_KEY_BOW_TYPE = "mcMMO: Bow Type";
     public static final @NotNull String METADATA_KEY_EXPLOSION_FROM_RUPTURE = "mcMMO: Rupture Explosion";
     public static final @NotNull String METADATA_KEY_FISH_HOOK_REF = "mcMMO: Fish Hook Tracker";
     public static final @NotNull String METADATA_KEY_DODGE_TRACKER = "mcMMO: Dodge Tracker";

+ 20 - 31
src/main/java/com/gmail/nossr50/util/skills/CombatUtils.java

@@ -52,8 +52,10 @@ public final class CombatUtils {
     // TODO: Unit tests
     public static void processProjectileSkillSuperAbilityActivation(McMMOPlayer mmoPlayer, ItemStack heldItem) {
         if (heldItem != null && mmoPlayer != null) {
-            if (ItemUtils.isBowOrCrossbow(heldItem))
-                mmoPlayer.checkAbilityActivationProjectiles(ItemUtils.getBowType(heldItem));
+            if (ItemUtils.isBowOrCrossbow(heldItem)) {
+                boolean isCrossbow = ItemUtils.isCrossbow(heldItem);
+                mmoPlayer.checkAbilityActivationProjectiles(isCrossbow);
+            }
         }
     }
 
@@ -159,14 +161,14 @@ public final class CombatUtils {
     }
 
     private static void processCrossbowsCombat(@NotNull LivingEntity target, @NotNull Player player,
-                                               @NotNull EntityDamageByEntityEvent event, @NotNull Projectile arrow) {
+                                               @NotNull EntityDamageByEntityEvent event, @NotNull Arrow arrow) {
         double initialDamage = event.getDamage();
 
         McMMOPlayer mcMMOPlayer = UserManager.getPlayer(player);
 
         //Make sure the profiles been loaded
         if(mcMMOPlayer == null) {
-            ProjectileUtils.cleanupProjectileMetadata(arrow);
+            delayArrowMetaCleanup(arrow);
             return;
         }
 
@@ -194,7 +196,7 @@ public final class CombatUtils {
                 "Final Damage: "+boostedDamage);
 
         //Clean data
-        ProjectileUtils.cleanupProjectileMetadata(arrow);
+        delayArrowMetaCleanup(arrow);
     }
 
     private static void processAxeCombat(@NotNull LivingEntity target, @NotNull Player player, @NotNull EntityDamageByEntityEvent event) {
@@ -324,14 +326,15 @@ public final class CombatUtils {
 
     }
 
-    private static void processArcheryCombat(@NotNull LivingEntity target, @NotNull Player player, @NotNull EntityDamageByEntityEvent event, @NotNull Projectile arrow) {
+    private static void processArcheryCombat(@NotNull LivingEntity target, @NotNull Player player,
+                                             @NotNull EntityDamageByEntityEvent event, @NotNull Arrow arrow) {
         double initialDamage = event.getDamage();
 
         McMMOPlayer mcMMOPlayer = UserManager.getPlayer(player);
 
         //Make sure the profiles been loaded
         if(mcMMOPlayer == null) {
-            ProjectileUtils.cleanupProjectileMetadata(arrow);
+            delayArrowMetaCleanup(arrow);
             return;
         }
 
@@ -372,7 +375,7 @@ public final class CombatUtils {
                 "Initial Damage: "+initialDamage,
                 "Final Damage: "+boostedDamage);
         //Clean data
-        ProjectileUtils.cleanupProjectileMetadata(arrow);
+        delayArrowMetaCleanup(arrow);
     }
 
     /**
@@ -489,22 +492,20 @@ public final class CombatUtils {
                 }
             }
         }
-        else if (entityType == EntityType.ARROW || entityType == EntityType.SPECTRAL_ARROW) {
-            Projectile arrow = (Projectile) painSource;
+        else if (painSource instanceof Arrow arrow) {
             ProjectileSource projectileSource = arrow.getShooter();
-
+            boolean isCrossbow = arrow.isShotFromCrossbow();
             if (projectileSource instanceof Player player) {
-                BowType bowType = getBowTypeFromMetadata(arrow);
 
                 if (!Misc.isNPCEntityExcludingVillagers(player)) {
-                    if(bowType == BowType.BOW && mcMMO.p.getSkillTools().canCombatSkillsTrigger(PrimarySkillType.ARCHERY, target)) {
+                    if(!isCrossbow && mcMMO.p.getSkillTools().canCombatSkillsTrigger(PrimarySkillType.ARCHERY, target)) {
                         processArcheryCombat(target, player, event, arrow);
-                    } else if(bowType == BowType.CROSSBOW && mcMMO.p.getSkillTools().canCombatSkillsTrigger(PrimarySkillType.CROSSBOWS, target)) {
+                    } else if(isCrossbow && mcMMO.p.getSkillTools().canCombatSkillsTrigger(PrimarySkillType.CROSSBOWS, target)) {
                         processCrossbowsCombat(target, player, event, arrow);
                     }
                 } else {
                     //Cleanup Arrow
-                    ProjectileUtils.cleanupProjectileMetadata(arrow);
+                    delayArrowMetaCleanup(arrow);
                 }
 
                 if (target.getType() != EntityType.CREEPER
@@ -522,18 +523,6 @@ public final class CombatUtils {
         }
     }
 
-    private static BowType getBowTypeFromMetadata(Projectile projectile) {
-        // Return the BowType from the metadata, or default to BOW
-        if (projectile.hasMetadata(MetadataConstants.METADATA_KEY_BOW_TYPE)) {
-            List<MetadataValue> metadataValue = projectile.getMetadata(MetadataConstants.METADATA_KEY_BOW_TYPE);
-
-            if (!metadataValue.isEmpty()) {
-                return (BowType) metadataValue.get(0).value();
-            }
-        }
-        throw new IllegalStateException("BowType metadata is empty");
-    }
-
     /**
      * This cleans up names from displaying in chat as hearts
      * @param entity target entity
@@ -728,7 +717,7 @@ public final class CombatUtils {
     }
 
     public static boolean hasIgnoreDamageMetadata(@NotNull LivingEntity target) {
-        return target.getMetadata(MetadataConstants.METADATA_KEY_CUSTOM_DAMAGE).size() != 0;
+        return target.hasMetadata(MetadataConstants.METADATA_KEY_CUSTOM_DAMAGE);
     }
 
     public static void dealNoInvulnerabilityTickDamageRupture(@NotNull LivingEntity target, double damage, Entity attacker, int toolTier) {
@@ -1040,9 +1029,9 @@ public final class CombatUtils {
     /**
      * Clean up metadata from a projectile after a minute has passed
      *
-     * @param entity the projectile
+     * @param arrow the projectile
      */
-    public static void delayArrowMetaCleanup(@NotNull Projectile entity) {
-        mcMMO.p.getFoliaLib().getImpl().runLater(() -> ProjectileUtils.cleanupProjectileMetadata(entity), 20*60);
+    public static void delayArrowMetaCleanup(@NotNull Arrow arrow) {
+        mcMMO.p.getFoliaLib().getImpl().runLater(() -> ProjectileUtils.cleanupProjectileMetadata(arrow), 20*120);
     }
 }

+ 11 - 15
src/main/java/com/gmail/nossr50/util/skills/ProjectileUtils.java

@@ -3,7 +3,7 @@ package com.gmail.nossr50.util.skills;
 import com.gmail.nossr50.mcMMO;
 import com.gmail.nossr50.util.MetadataConstants;
 import org.bukkit.block.BlockFace;
-import org.bukkit.entity.Projectile;
+import org.bukkit.entity.Arrow;
 import org.bukkit.util.Vector;
 import org.jetbrains.annotations.NotNull;
 
@@ -23,28 +23,24 @@ public class ProjectileUtils {
     /**
      * Clean up all possible mcMMO related metadata for a projectile
      *
-     * @param entity projectile
+     * @param arrow projectile
      */
     // TODO: Add test
-    public static void cleanupProjectileMetadata(@NotNull Projectile entity) {
-        if(entity.hasMetadata(MetadataConstants.METADATA_KEY_INF_ARROW)) {
-            entity.removeMetadata(MetadataConstants.METADATA_KEY_INF_ARROW, mcMMO.p);
+    public static void cleanupProjectileMetadata(@NotNull Arrow arrow) {
+        if(arrow.hasMetadata(MetadataConstants.METADATA_KEY_INF_ARROW)) {
+            arrow.removeMetadata(MetadataConstants.METADATA_KEY_INF_ARROW, mcMMO.p);
         }
 
-        if(entity.hasMetadata(MetadataConstants.METADATA_KEY_BOW_FORCE)) {
-            entity.removeMetadata(MetadataConstants.METADATA_KEY_BOW_FORCE, mcMMO.p);
+        if(arrow.hasMetadata(MetadataConstants.METADATA_KEY_BOW_FORCE)) {
+            arrow.removeMetadata(MetadataConstants.METADATA_KEY_BOW_FORCE, mcMMO.p);
         }
 
-        if(entity.hasMetadata(MetadataConstants.METADATA_KEY_ARROW_DISTANCE)) {
-            entity.removeMetadata(MetadataConstants.METADATA_KEY_ARROW_DISTANCE, mcMMO.p);
+        if(arrow.hasMetadata(MetadataConstants.METADATA_KEY_ARROW_DISTANCE)) {
+            arrow.removeMetadata(MetadataConstants.METADATA_KEY_ARROW_DISTANCE, mcMMO.p);
         }
 
-        if(entity.hasMetadata(MetadataConstants.METADATA_KEY_BOW_TYPE)) {
-            entity.removeMetadata(MetadataConstants.METADATA_KEY_BOW_TYPE, mcMMO.p);
-        }
-
-        if(entity.hasMetadata(MetadataConstants.METADATA_KEY_SPAWNED_ARROW)) {
-            entity.removeMetadata(MetadataConstants.METADATA_KEY_SPAWNED_ARROW, mcMMO.p);
+        if(arrow.hasMetadata(MetadataConstants.METADATA_KEY_SPAWNED_ARROW)) {
+            arrow.removeMetadata(MetadataConstants.METADATA_KEY_SPAWNED_ARROW, mcMMO.p);
         }
     }
 }

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

@@ -859,6 +859,8 @@ permissions:
             mcmmo.commands.alchemy: true
             mcmmo.commands.archery: true
             mcmmo.commands.axes: true
+            mcmmo.commands.crossbows: true
+            mcmmo.commands.tridents: true
             mcmmo.commands.excavation: true
             mcmmo.commands.fishing: true
             mcmmo.commands.herbalism: true
@@ -2212,6 +2214,8 @@ permissions:
             mcmmo.skills.taming: true
             mcmmo.skills.unarmed: true
             mcmmo.skills.woodcutting: true
+            mcmmo.skills.crossbows: true
+            mcmmo.skills.tridents: true
     mcmmo.skills.acrobatics:
         description: Allows access to the Acrobatics skill
         children: