Initial work on crossbows

This commit is contained in:
nossr50 2020-07-07 15:00:56 -07:00
parent cf6af4630d
commit 7543d8be12
7 changed files with 152 additions and 12 deletions

View File

@ -17,6 +17,7 @@ Version 2.1.133
Smelting now has a Bonus Drops section in config.yml Smelting now has a Bonus Drops section in config.yml
Second Smelt now only doubles smelting results for items which have bonus drop entries in the config Second Smelt now only doubles smelting results for items which have bonus drop entries in the config
Fixed an array out of index bug for inventory click events Fixed an array out of index bug for inventory click events
mcMMO will now register arrows shot from the offhand as being from either Archery or Crossbows (before mcMMO ignored offhand Archery)
(These permissions are all included in the mcmmo.defaults node) (These permissions are all included in the mcmmo.defaults node)
New permission node 'mcmmo.commands.tridents' New permission node 'mcmmo.commands.tridents'

View File

@ -0,0 +1,17 @@
package com.gmail.nossr50.datatypes.meta;
import org.bukkit.metadata.FixedMetadataValue;
import org.bukkit.plugin.Plugin;
import org.jetbrains.annotations.NotNull;
public class ProjectileOriginMeta extends FixedMetadataValue {
/**
* Initializes a FixedMetadataValue with an Object
*
* @param owningPlugin the {@link Plugin} that created this metadata value
* @param value the value assigned to this metadata value
*/
public ProjectileOriginMeta(@NotNull Plugin owningPlugin, int value) {
super(owningPlugin, value);
}
}

View File

@ -4,6 +4,7 @@ import com.gmail.nossr50.config.AdvancedConfig;
import com.gmail.nossr50.config.Config; import com.gmail.nossr50.config.Config;
import com.gmail.nossr50.config.WorldBlacklist; import com.gmail.nossr50.config.WorldBlacklist;
import com.gmail.nossr50.config.experience.ExperienceConfig; import com.gmail.nossr50.config.experience.ExperienceConfig;
import com.gmail.nossr50.datatypes.meta.ProjectileOriginMeta;
import com.gmail.nossr50.datatypes.player.McMMOPlayer; import com.gmail.nossr50.datatypes.player.McMMOPlayer;
import com.gmail.nossr50.datatypes.skills.SubSkillType; import com.gmail.nossr50.datatypes.skills.SubSkillType;
import com.gmail.nossr50.datatypes.skills.subskills.interfaces.InteractType; import com.gmail.nossr50.datatypes.skills.subskills.interfaces.InteractType;
@ -19,6 +20,7 @@ import com.gmail.nossr50.skills.taming.Taming;
import com.gmail.nossr50.skills.taming.TamingManager; import com.gmail.nossr50.skills.taming.TamingManager;
import com.gmail.nossr50.skills.unarmed.UnarmedManager; import com.gmail.nossr50.skills.unarmed.UnarmedManager;
import com.gmail.nossr50.util.BlockUtils; import com.gmail.nossr50.util.BlockUtils;
import com.gmail.nossr50.util.ItemUtils;
import com.gmail.nossr50.util.Misc; import com.gmail.nossr50.util.Misc;
import com.gmail.nossr50.util.Permissions; import com.gmail.nossr50.util.Permissions;
import com.gmail.nossr50.util.player.NotificationManager; import com.gmail.nossr50.util.player.NotificationManager;
@ -122,7 +124,7 @@ public class EntityListener implements Listener {
projectile.setMetadata(mcMMO.arrowDistanceKey, new FixedMetadataValue(plugin, projectile.getLocation())); projectile.setMetadata(mcMMO.arrowDistanceKey, new FixedMetadataValue(plugin, projectile.getLocation()));
} }
@EventHandler(priority = EventPriority.MONITOR, ignoreCancelled = true) @EventHandler(ignoreCancelled = true, priority = EventPriority.MONITOR)
public void onProjectileLaunch(ProjectileLaunchEvent event) { public void onProjectileLaunch(ProjectileLaunchEvent event) {
/* WORLD BLACKLIST CHECK */ /* WORLD BLACKLIST CHECK */
if(WorldBlacklist.isWorldBlacklisted(event.getEntity().getWorld())) if(WorldBlacklist.isWorldBlacklisted(event.getEntity().getWorld()))
@ -144,9 +146,19 @@ public class EntityListener implements Listener {
if(!(projectile instanceof Arrow)) if(!(projectile instanceof Arrow))
return; return;
projectile.setMetadata(mcMMO.bowForceKey, new FixedMetadataValue(plugin, 1.0));
projectile.setMetadata(mcMMO.arrowDistanceKey, new FixedMetadataValue(plugin, projectile.getLocation())); projectile.setMetadata(mcMMO.arrowDistanceKey, new FixedMetadataValue(plugin, projectile.getLocation()));
//Track origin of projectile
if(ItemUtils.hasItemInMainHand(player, "bow")) {
markProjectileOriginAsBow(projectile);
} else if(ItemUtils.hasItemInMainHand(player, "crossbow")) {
markProjectileOriginAsCrossbow(projectile);
} else if(ItemUtils.hasItemInOffHand(player, "bow")) {
markProjectileOriginAsBow(projectile);
} else if(ItemUtils.hasItemInOffHand(player, "crossbow")) {
markProjectileOriginAsCrossbow(projectile);
}
for(Enchantment enchantment : player.getInventory().getItemInMainHand().getEnchantments().keySet()) { for(Enchantment enchantment : player.getInventory().getItemInMainHand().getEnchantments().keySet()) {
if(enchantment.getName().equalsIgnoreCase("piercing")) if(enchantment.getName().equalsIgnoreCase("piercing"))
return; return;
@ -158,6 +170,14 @@ public class EntityListener implements Listener {
} }
} }
private void markProjectileOriginAsCrossbow(Projectile projectile) {
projectile.setMetadata(mcMMO.PROJECTILE_ORIGIN_METAKEY, new ProjectileOriginMeta(plugin, 2));
}
private void markProjectileOriginAsBow(Projectile projectile) {
projectile.setMetadata(mcMMO.PROJECTILE_ORIGIN_METAKEY, new ProjectileOriginMeta(plugin, 1));
}
/** /**
* Monitor EntityChangeBlock events. * Monitor EntityChangeBlock events.
* *

View File

@ -134,6 +134,7 @@ public class mcMMO extends JavaPlugin {
public final static String greenThumbDataKey = "mcMMO: Green Thumb"; public final static String greenThumbDataKey = "mcMMO: Green Thumb";
public final static String databaseCommandKey = "mcMMO: Processing Database Command"; public final static String databaseCommandKey = "mcMMO: Processing Database Command";
public final static String bredMetadataKey = "mcMMO: Bred Animal"; public final static String bredMetadataKey = "mcMMO: Bred Animal";
public final static String PROJECTILE_ORIGIN_METAKEY = "mcMMO: Projectile Origin";
public static FixedMetadataValue metadataValue; public static FixedMetadataValue metadataValue;

View File

@ -2,10 +2,38 @@ package com.gmail.nossr50.skills.crossbows;
import com.gmail.nossr50.datatypes.player.McMMOPlayer; import com.gmail.nossr50.datatypes.player.McMMOPlayer;
import com.gmail.nossr50.datatypes.skills.PrimarySkillType; import com.gmail.nossr50.datatypes.skills.PrimarySkillType;
import com.gmail.nossr50.mcMMO;
import com.gmail.nossr50.skills.SkillManager; import com.gmail.nossr50.skills.SkillManager;
import com.gmail.nossr50.skills.archery.Archery;
import org.bukkit.Location;
import org.bukkit.entity.Entity;
import org.bukkit.entity.LivingEntity;
public class CrossbowManager extends SkillManager { public class CrossbowManager extends SkillManager {
public CrossbowManager(McMMOPlayer mcMMOPlayer) { public CrossbowManager(McMMOPlayer mcMMOPlayer) {
super(mcMMOPlayer, PrimarySkillType.CROSSBOWS); super(mcMMOPlayer, PrimarySkillType.CROSSBOWS);
} }
/**
* Calculate bonus XP awarded for Archery when hitting a far-away target.
*
* @param target The {@link LivingEntity} damaged by the arrow
* @param damager The {@link Entity} who shot the arrow
*/
public double distanceXpBonusMultiplier(LivingEntity target, Entity damager) {
//Hacky Fix - some plugins spawn arrows and assign them to players after the ProjectileLaunchEvent fires
if(!damager.hasMetadata(mcMMO.arrowDistanceKey))
return damager.getLocation().distance(target.getLocation());
Location firedLocation = (Location) damager.getMetadata(mcMMO.arrowDistanceKey).get(0).value();
Location targetLocation = target.getLocation();
if (firedLocation.getWorld() != targetLocation.getWorld()) {
return 1;
}
//TODO: Should use its own variable
return 1 + Math.min(firedLocation.distance(targetLocation), 50) * Archery.DISTANCE_XP_MULTIPLIER;
}
} }

View File

@ -31,9 +31,23 @@ public final class ItemUtils {
public static boolean hasItemInEitherHand(Player player, Material material) { public static boolean hasItemInEitherHand(Player player, Material material) {
return player.getInventory().getItemInMainHand().getType() == material || player.getInventory().getItemInOffHand().getType() == material; return hasItemInEitherHand(player, material.getKey().getKey());
} }
public static boolean hasItemInEitherHand(Player player, String id) {
return player.getInventory().getItemInMainHand().getType().getKey().getKey().equalsIgnoreCase(id)
|| player.getInventory().getItemInOffHand().getType().getKey().getKey().equalsIgnoreCase(id);
}
public static boolean hasItemInMainHand(Player player, String id) {
return player.getInventory().getItemInMainHand().getType().getKey().getKey().equalsIgnoreCase(id);
}
public static boolean hasItemInOffHand(Player player, String id) {
return player.getInventory().getItemInOffHand().getType().getKey().getKey().equalsIgnoreCase(id);
}
/** /**
* Checks if the item is a sword. * Checks if the item is a sword.
* *

View File

@ -16,6 +16,7 @@ import com.gmail.nossr50.runnables.skills.AwardCombatXpTask;
import com.gmail.nossr50.skills.acrobatics.AcrobaticsManager; import com.gmail.nossr50.skills.acrobatics.AcrobaticsManager;
import com.gmail.nossr50.skills.archery.ArcheryManager; import com.gmail.nossr50.skills.archery.ArcheryManager;
import com.gmail.nossr50.skills.axes.AxesManager; import com.gmail.nossr50.skills.axes.AxesManager;
import com.gmail.nossr50.skills.crossbows.CrossbowManager;
import com.gmail.nossr50.skills.swords.SwordsManager; import com.gmail.nossr50.skills.swords.SwordsManager;
import com.gmail.nossr50.skills.taming.TamingManager; import com.gmail.nossr50.skills.taming.TamingManager;
import com.gmail.nossr50.skills.tridents.TridentManager; import com.gmail.nossr50.skills.tridents.TridentManager;
@ -308,13 +309,49 @@ public final class CombatUtils {
} }
double distanceMultiplier = archeryManager.distanceXpBonusMultiplier(target, arrow); double distanceMultiplier = archeryManager.distanceXpBonusMultiplier(target, arrow);
double forceMultiplier = 1.0; //Hacky Fix - some plugins spawn arrows and assign them to players after the ProjectileLaunchEvent fires // double forceMultiplier = 1.0; //Hacky Fix - some plugins spawn arrows and assign them to players after the ProjectileLaunchEvent fires
if(arrow.hasMetadata(mcMMO.bowForceKey)) // if(arrow.hasMetadata(mcMMO.bowForceKey))
forceMultiplier = arrow.getMetadata(mcMMO.bowForceKey).get(0).asDouble(); // forceMultiplier = arrow.getMetadata(mcMMO.bowForceKey).get(0).asDouble();
applyScaledModifiers(initialDamage, finalDamage, event); applyScaledModifiers(initialDamage, finalDamage, event);
processCombatXP(mcMMOPlayer, target, PrimarySkillType.ARCHERY, forceMultiplier * distanceMultiplier); processCombatXP(mcMMOPlayer, target, PrimarySkillType.ARCHERY, distanceMultiplier);
}
private static void processCrossbowCombat(LivingEntity target, Player player, EntityDamageByEntityEvent event, Projectile arrow) {
double initialDamage = event.getDamage();
McMMOPlayer mcMMOPlayer = UserManager.getPlayer(player);
//Make sure the profiles been loaded
if(mcMMOPlayer == null) {
return;
}
CrossbowManager crossbowManager = mcMMOPlayer.getCrossbowManager();
double finalDamage = event.getDamage();
if (target instanceof Player && PrimarySkillType.UNARMED.getPVPEnabled()) {
UnarmedManager unarmedManager = UserManager.getPlayer((Player) target).getUnarmedManager();
if (unarmedManager.canDeflect()) {
event.setCancelled(unarmedManager.deflectCheck());
if (event.isCancelled()) {
return;
}
}
}
if(canUseLimitBreak(player, target, SubSkillType.CROSSBOWS_CROSSBOWS_LIMIT_BREAK))
{
finalDamage+=getLimitBreakDamage(player, target, SubSkillType.CROSSBOWS_CROSSBOWS_LIMIT_BREAK);
}
double distanceMultiplier = crossbowManager.distanceXpBonusMultiplier(target, arrow);
applyScaledModifiers(initialDamage, finalDamage, event);
processCombatXP(mcMMOPlayer, target, PrimarySkillType.CROSSBOWS, distanceMultiplier);
} }
/** /**
@ -433,18 +470,32 @@ public final class CombatUtils {
} }
else if (entityType == EntityType.ARROW || entityType == EntityType.SPECTRAL_ARROW) { else if (entityType == EntityType.ARROW || entityType == EntityType.SPECTRAL_ARROW) {
Projectile arrow = (Projectile) painSource; Projectile arrow = (Projectile) painSource;
ProjectileSource projectileSource = arrow.getShooter(); ProjectileSource projectileShooter = arrow.getShooter();
//Determine if the arrow belongs to a bow or xbow //Determine if the arrow belongs to a bow or xbow
if (projectileSource instanceof Player && PrimarySkillType.ARCHERY.shouldProcess(target)) { if (projectileShooter instanceof Player) {
Player player = (Player) projectileSource; Player player = (Player) projectileShooter;
if (!Misc.isNPCEntityExcludingVillagers(player) && PrimarySkillType.ARCHERY.getPermissions(player)) { //Has metadata
processArcheryCombat(target, player, event, arrow); if(arrow.getMetadata(mcMMO.PROJECTILE_ORIGIN_METAKEY).size() > 0) {
if(isProjectileFromBow(arrow)) {
if(PrimarySkillType.ARCHERY.shouldProcess(target)) {
if (!Misc.isNPCEntityExcludingVillagers(player) && PrimarySkillType.ARCHERY.getPermissions(player)) {
processArcheryCombat(target, player, event, arrow);
}
}
} else if(isProjectileFromCrossbow(arrow)) {
if(PrimarySkillType.CROSSBOWS.shouldProcess(target)) {
if (!Misc.isNPCEntityExcludingVillagers(player) && PrimarySkillType.CROSSBOWS.getPermissions(player)) {
processCrossbowCombat(target, player, event, arrow);
}
}
}
} }
if (target.getType() != EntityType.CREEPER && !Misc.isNPCEntityExcludingVillagers(player) && PrimarySkillType.TAMING.getPermissions(player)) { if (target.getType() != EntityType.CREEPER && !Misc.isNPCEntityExcludingVillagers(player) && PrimarySkillType.TAMING.getPermissions(player)) {
McMMOPlayer mcMMOPlayer = UserManager.getPlayer(player); McMMOPlayer mcMMOPlayer = UserManager.getPlayer(player);
TamingManager tamingManager = mcMMOPlayer.getTamingManager(); TamingManager tamingManager = mcMMOPlayer.getTamingManager();
@ -454,6 +505,14 @@ public final class CombatUtils {
} }
} }
private static boolean isProjectileFromCrossbow(Projectile arrow) {
return arrow.getMetadata(mcMMO.PROJECTILE_ORIGIN_METAKEY).get(0).asInt() == 2;
}
private static boolean isProjectileFromBow(Projectile arrow) {
return arrow.getMetadata(mcMMO.PROJECTILE_ORIGIN_METAKEY).get(0).asInt() == 1;
}
/** /**
* This cleans up names from displaying in chat as hearts * This cleans up names from displaying in chat as hearts
* @param entity target entity * @param entity target entity