Crossbow Fixes

This commit is contained in:
nossr50 2024-01-22 13:51:23 -08:00
parent f051edd03d
commit b3b8a12b6d
13 changed files with 76 additions and 112 deletions

View File

@ -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>

View File

@ -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 {

View File

@ -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)) {

View File

@ -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);
}
}
}
}

View File

@ -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()));
}
}

View File

@ -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;
}

View File

@ -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() {

View File

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

View File

@ -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());

View File

@ -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";

View File

@ -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);
}
}

View File

@ -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);
}
}
}

View File

@ -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: