Improved staff attacks, new ray trace stuff

This commit is contained in:
Indyuce 2022-02-26 13:37:34 +01:00
parent a9bd80fac3
commit b93f90f205
11 changed files with 116 additions and 124 deletions

View File

@ -21,67 +21,66 @@ import org.bukkit.entity.Player;
import org.bukkit.util.Vector;
public class Musket extends UntargetedWeapon {
public Musket(Player player, NBTItem item) {
super(player, item, WeaponType.RIGHT_CLICK);
}
public Musket(Player player, NBTItem item) {
super(player, item, WeaponType.RIGHT_CLICK);
}
@Override
public void untargetedAttack(EquipmentSlot slot) {
@Override
public void untargetedAttack(EquipmentSlot slot) {
UntargetedDurabilityItem durItem = new UntargetedDurabilityItem(getPlayer(), getNBTItem(), slot);
if (durItem.isBroken())
return;
UntargetedDurabilityItem durItem = new UntargetedDurabilityItem(getPlayer(), getNBTItem(), slot);
if (durItem.isBroken())
return;
PlayerMetadata stats = getPlayerData().getStats().newTemporary(slot);
if (!applyWeaponCosts(1 / getValue(stats.getStat("ATTACK_SPEED"), MMOItems.plugin.getConfig().getDouble("default.attack-speed")),
CooldownType.ATTACK))
return;
PlayerMetadata stats = getPlayerData().getStats().newTemporary(slot);
if (!applyWeaponCosts(1 / getValue(stats.getStat("ATTACK_SPEED"), MMOItems.plugin.getConfig().getDouble("default.attack-speed")),
CooldownType.ATTACK))
return;
if (durItem.isValid())
durItem.decreaseDurability(1).inventoryUpdate();
if (durItem.isValid())
durItem.decreaseDurability(1).inventoryUpdate();
double attackDamage = stats.getStat("ATTACK_DAMAGE");
double range = getValue(getNBTItem().getStat(ItemStats.RANGE.getId()), MMOItems.plugin.getConfig().getDouble("default.range"));
double recoil = getValue(getNBTItem().getStat(ItemStats.RECOIL.getId()), MMOItems.plugin.getConfig().getDouble("default.recoil"));
double attackDamage = stats.getStat("ATTACK_DAMAGE");
double range = getValue(getNBTItem().getStat(ItemStats.RANGE.getId()), MMOItems.plugin.getConfig().getDouble("default.range"));
double recoil = getValue(getNBTItem().getStat(ItemStats.RECOIL.getId()), MMOItems.plugin.getConfig().getDouble("default.recoil"));
// knockback
double knockback = getNBTItem().getStat(ItemStats.KNOCKBACK.getId());
if (knockback > 0)
getPlayer().setVelocity(getPlayer().getVelocity()
.add(getPlayer().getEyeLocation().getDirection().setY(0).normalize().multiply(-1 * knockback).setY(-.2)));
// knockback
double knockback = getNBTItem().getStat(ItemStats.KNOCKBACK.getId());
if (knockback > 0)
getPlayer().setVelocity(getPlayer().getVelocity()
.add(getPlayer().getEyeLocation().getDirection().setY(0).normalize().multiply(-1 * knockback).setY(-.2)));
double a = Math.toRadians(getPlayer().getEyeLocation().getYaw() + 160);
Location loc = getPlayer().getEyeLocation().add(new Vector(Math.cos(a), 0, Math.sin(a)).multiply(.5));
double a = Math.toRadians(getPlayer().getEyeLocation().getYaw() + 180 + 30 * (slot == EquipmentSlot.MAIN_HAND ? -1 : 1));
Location loc = getPlayer().getEyeLocation().add(new Vector(Math.cos(a), 0, Math.sin(a)).multiply(.5));
loc.setPitch((float) (loc.getPitch() + (RANDOM.nextDouble() - .5) * 2 * recoil));
loc.setYaw((float) (loc.getYaw() + (RANDOM.nextDouble() - .5) * 2 * recoil));
Vector vec = loc.getDirection();
loc.setPitch((float) (loc.getPitch() + (RANDOM.nextDouble() - .5) * 2 * recoil));
loc.setYaw((float) (loc.getYaw() + (RANDOM.nextDouble() - .5) * 2 * recoil));
Vector vec = loc.getDirection();
RayTrace trace = new RayTrace(loc, vec, range,
entity -> MMOUtils.canTarget(stats.getPlayer(), entity, InteractionType.OFFENSE_ACTION));
if (trace.hasHit()) {
ItemAttackMetadata attackMeta = new ItemAttackMetadata(new DamageMetadata(attackDamage, DamageType.WEAPON, DamageType.PROJECTILE, DamageType.PHYSICAL), stats);
attackMeta.applyEffectsAndDamage(getNBTItem(), trace.getHit());
}
RayTrace trace = new RayTrace(loc, vec, range, entity -> MMOUtils.canTarget(stats.getPlayer(), entity, InteractionType.OFFENSE_ACTION));
if (trace.hasHit()) {
ItemAttackMetadata attackMeta = new ItemAttackMetadata(new DamageMetadata(attackDamage, DamageType.WEAPON, DamageType.PROJECTILE, DamageType.PHYSICAL), stats);
attackMeta.applyEffectsAndDamage(getNBTItem(), trace.getHit());
}
trace.draw(2, Color.BLACK);
getPlayer().getWorld().playSound(getPlayer().getLocation(), Sound.ENTITY_ZOMBIE_ATTACK_IRON_DOOR, 2, 2);
}
trace.draw(2, Color.BLACK);
getPlayer().getWorld().playSound(getPlayer().getLocation(), Sound.ENTITY_ZOMBIE_ATTACK_IRON_DOOR, 2, 2);
}
@Override
public LivingEntity untargetedTargetTrace(EquipmentSlot slot) {
@Override
public LivingEntity untargetedTargetTrace(EquipmentSlot slot) {
// Temporary Stats
PlayerMetadata stats = getPlayerData().getStats().newTemporary(slot);
// Temporary Stats
PlayerMetadata stats = getPlayerData().getStats().newTemporary(slot);
// Range
double range = getValue(getNBTItem().getStat(ItemStats.RANGE.getId()), MMOItems.plugin.getConfig().getDouble("default.range"));
double a = Math.toRadians(getPlayer().getEyeLocation().getYaw() + 160);
Location loc = getPlayer().getEyeLocation().add(new Vector(Math.cos(a), 0, Math.sin(a)).multiply(.5));
Vector vec = loc.getDirection();
// Range
double range = getValue(getNBTItem().getStat(ItemStats.RANGE.getId()), MMOItems.plugin.getConfig().getDouble("default.range"));
double a = Math.toRadians(getPlayer().getEyeLocation().getYaw() + 160);
Location loc = getPlayer().getEyeLocation().add(new Vector(Math.cos(a), 0, Math.sin(a)).multiply(.5));
Vector vec = loc.getDirection();
// Raytrace
return new RayTrace(loc, vec, range,
entity -> MMOUtils.canTarget(stats.getPlayer(), entity, InteractionType.OFFENSE_ACTION)).getHit();
}
// Raytrace
return new RayTrace(loc, vec, range,
entity -> MMOUtils.canTarget(stats.getPlayer(), entity, InteractionType.OFFENSE_ACTION)).getHit();
}
}

View File

@ -16,7 +16,6 @@ import net.Indyuce.mmoitems.api.interaction.util.UntargetedDurabilityItem;
import net.Indyuce.mmoitems.api.player.PlayerData.CooldownType;
import net.Indyuce.mmoitems.stat.StaffSpiritStat.StaffSpirit;
import org.bukkit.EntityEffect;
import org.bukkit.Location;
import org.bukkit.Particle;
import org.bukkit.Sound;
import org.bukkit.entity.LivingEntity;
@ -51,15 +50,11 @@ public class Staff extends UntargetedWeapon {
StaffSpirit spirit = StaffSpirit.get(getNBTItem());
if (spirit != null) {
spirit.getAttack().handle(attackMeta, getNBTItem(), range);
spirit.getAttack().handle(attackMeta, getNBTItem(), slot, range);
return;
}
double a = Math.toRadians(getPlayer().getEyeLocation().getYaw() + 160);
Location loc = getPlayer().getEyeLocation().add(new Vector(Math.cos(a), 0, Math.sin(a)).multiply(.5));
RayTrace trace = new RayTrace(stats.getPlayer(), range,
entity -> MMOUtils.canTarget(stats.getPlayer(), entity, InteractionType.OFFENSE_ACTION));
RayTrace trace = new RayTrace(stats.getPlayer(), slot, range, entity -> MMOUtils.canTarget(stats.getPlayer(), entity, InteractionType.OFFENSE_ACTION));
if (trace.hasHit())
attackMeta.applyEffectsAndDamage(getNBTItem(), trace.getHit());
trace.draw(2, tick -> tick.getWorld().spawnParticle(Particle.EXPLOSION_NORMAL, tick, 0, .1, .1, .1, 0));

View File

@ -21,52 +21,48 @@ import org.bukkit.entity.Player;
import org.bukkit.util.Vector;
public class Whip extends UntargetedWeapon {
public Whip(Player player, NBTItem item) {
super(player, item, WeaponType.LEFT_CLICK);
}
public Whip(Player player, NBTItem item) {
super(player, item, WeaponType.LEFT_CLICK);
}
@Override
public void untargetedAttack(EquipmentSlot slot) {
@Override
public void untargetedAttack(EquipmentSlot slot) {
UntargetedDurabilityItem durItem = new UntargetedDurabilityItem(getPlayer(), getNBTItem(), slot);
if (durItem.isBroken())
return;
UntargetedDurabilityItem durItem = new UntargetedDurabilityItem(getPlayer(), getNBTItem(), slot);
if (durItem.isBroken())
return;
PlayerMetadata stats = getPlayerData().getStats().newTemporary(slot);
if (!applyWeaponCosts(1 / getValue(stats.getStat("ATTACK_SPEED"), MMOItems.plugin.getConfig().getDouble("default.attack-speed")),
CooldownType.ATTACK))
return;
PlayerMetadata stats = getPlayerData().getStats().newTemporary(slot);
if (!applyWeaponCosts(1 / getValue(stats.getStat("ATTACK_SPEED"), MMOItems.plugin.getConfig().getDouble("default.attack-speed")),
CooldownType.ATTACK))
return;
if (durItem.isValid())
durItem.decreaseDurability(1).inventoryUpdate();
if (durItem.isValid())
durItem.decreaseDurability(1).inventoryUpdate();
double attackDamage = getValue(stats.getStat("ATTACK_DAMAGE"), 7);
double range = getValue(getNBTItem().getStat(ItemStats.RANGE.getId()), MMOItems.plugin.getConfig().getDouble("default.range"));
double attackDamage = getValue(stats.getStat("ATTACK_DAMAGE"), 7);
double range = getValue(getNBTItem().getStat(ItemStats.RANGE.getId()), MMOItems.plugin.getConfig().getDouble("default.range"));
double a = Math.toRadians(getPlayer().getEyeLocation().getYaw() + 160);
Location loc = getPlayer().getEyeLocation().add(new Vector(Math.cos(a), 0, Math.sin(a)).multiply(.5));
RayTrace trace = new RayTrace(getPlayer(), slot, range, entity -> MMOUtils.canTarget(stats.getPlayer(), entity, InteractionType.OFFENSE_ACTION));
if (trace.hasHit())
new ItemAttackMetadata(new DamageMetadata(attackDamage, DamageType.WEAPON, DamageType.PROJECTILE, DamageType.PHYSICAL), stats).applyEffectsAndDamage(getNBTItem(), trace.getHit());
trace.draw(2, tick -> tick.getWorld().spawnParticle(Particle.CRIT, tick, 0, .1, .1, .1, 0));
getPlayer().getWorld().playSound(getPlayer().getLocation(), VersionSound.ENTITY_FIREWORK_ROCKET_BLAST.toSound(), 1, 2);
}
RayTrace trace = new RayTrace(loc, getPlayer().getEyeLocation().getDirection(), range,
entity -> MMOUtils.canTarget(stats.getPlayer(), entity, InteractionType.OFFENSE_ACTION));
if (trace.hasHit())
new ItemAttackMetadata(new DamageMetadata(attackDamage, DamageType.WEAPON, DamageType.PROJECTILE, DamageType.PHYSICAL), stats).applyEffectsAndDamage(getNBTItem(), trace.getHit());
trace.draw(2, tick -> tick.getWorld().spawnParticle(Particle.CRIT, tick, 0, .1, .1, .1, 0));
getPlayer().getWorld().playSound(getPlayer().getLocation(), VersionSound.ENTITY_FIREWORK_ROCKET_BLAST.toSound(), 1, 2);
}
@Override
public LivingEntity untargetedTargetTrace(EquipmentSlot slot) {
@Override
public LivingEntity untargetedTargetTrace(EquipmentSlot slot) {
// Temporary Stats
PlayerMetadata stats = getPlayerData().getStats().newTemporary(slot);
// Temporary Stats
PlayerMetadata stats = getPlayerData().getStats().newTemporary(slot);
// Range
double range = getValue(getNBTItem().getStat(ItemStats.RANGE.getId()), MMOItems.plugin.getConfig().getDouble("default.range"));
double a = Math.toRadians(getPlayer().getEyeLocation().getYaw() + 160);
Location loc = getPlayer().getEyeLocation().add(new Vector(Math.cos(a), 0, Math.sin(a)).multiply(.5));
// Range
double range = getValue(getNBTItem().getStat(ItemStats.RANGE.getId()), MMOItems.plugin.getConfig().getDouble("default.range"));
double a = Math.toRadians(getPlayer().getEyeLocation().getYaw() + 160);
Location loc = getPlayer().getEyeLocation().add(new Vector(Math.cos(a), 0, Math.sin(a)).multiply(.5));
// Ray Trace
return new RayTrace(stats.getPlayer(), range,
entity -> MMOUtils.canTarget(stats.getPlayer(), entity, InteractionType.OFFENSE_ACTION)).getHit();
}
// Ray Trace
return new RayTrace(stats.getPlayer(), range,
entity -> MMOUtils.canTarget(stats.getPlayer(), entity, InteractionType.OFFENSE_ACTION)).getHit();
}
}

View File

@ -1,22 +1,21 @@
package net.Indyuce.mmoitems.api.interaction.weapon.untargeted.staff;
import io.lumine.mythic.lib.api.item.NBTItem;
import io.lumine.mythic.lib.api.player.EquipmentSlot;
import io.lumine.mythic.lib.comp.target.InteractionType;
import io.lumine.mythic.lib.util.RayTrace;
import io.lumine.mythic.lib.version.VersionSound;
import net.Indyuce.mmoitems.MMOUtils;
import net.Indyuce.mmoitems.api.ItemAttackMetadata;
import org.bukkit.Location;
import org.bukkit.Particle;
public class LightningSpirit implements StaffAttackHandler {
@Override
public void handle(ItemAttackMetadata attackMeta, NBTItem nbt, double range) {
public void handle(ItemAttackMetadata attackMeta, NBTItem nbt, EquipmentSlot slot, double range) {
attackMeta.getPlayer().getWorld().playSound(attackMeta.getPlayer().getLocation(), VersionSound.ENTITY_FIREWORK_ROCKET_BLAST.toSound(), 2, 2);
RayTrace trace = new RayTrace(attackMeta.getPlayer(), range,
entity -> MMOUtils.canTarget(attackMeta.getPlayer(), entity, InteractionType.OFFENSE_ACTION));
RayTrace trace = new RayTrace(attackMeta.getPlayer(), slot, range, entity -> MMOUtils.canTarget(attackMeta.getPlayer(), entity, InteractionType.OFFENSE_ACTION));
if (trace.hasHit())
attackMeta.applyEffectsAndDamage(nbt, trace.getHit());

View File

@ -1,6 +1,7 @@
package net.Indyuce.mmoitems.api.interaction.weapon.untargeted.staff;
import io.lumine.mythic.lib.api.item.NBTItem;
import io.lumine.mythic.lib.api.player.EquipmentSlot;
import io.lumine.mythic.lib.comp.target.InteractionType;
import net.Indyuce.mmoitems.MMOItems;
import net.Indyuce.mmoitems.MMOUtils;
@ -19,7 +20,7 @@ import java.util.List;
public class ManaSpirit implements StaffAttackHandler {
@Override
public void handle(ItemAttackMetadata attackMeta, NBTItem nbt, double range) {
public void handle(ItemAttackMetadata attackMeta, NBTItem nbt, EquipmentSlot slot, double range) {
new BukkitRunnable() {
final Vector vec = attackMeta.getPlayer().getEyeLocation().getDirection().multiply(.4);
final Location loc = attackMeta.getPlayer().getEyeLocation();
@ -42,7 +43,7 @@ public class ManaSpirit implements StaffAttackHandler {
for (double item = 0; item < Math.PI * 2; item += Math.PI / 3.5) {
Vector vec = MMOUtils.rotateFunc(new Vector(r * Math.cos(item), r * Math.sin(item), 0), loc);
if (random.nextDouble() <= .6)
if (RANDOM.nextDouble() <= .6)
loc.getWorld().spawnParticle(Particle.REDSTONE, loc.clone().add(vec), 1, new Particle.DustOptions(Color.AQUA, 1));
}
for (Entity target : targets)

View File

@ -1,6 +1,7 @@
package net.Indyuce.mmoitems.api.interaction.weapon.untargeted.staff;
import io.lumine.mythic.lib.api.item.NBTItem;
import io.lumine.mythic.lib.api.player.EquipmentSlot;
import io.lumine.mythic.lib.comp.target.InteractionType;
import net.Indyuce.mmoitems.MMOItems;
import net.Indyuce.mmoitems.MMOUtils;
@ -18,7 +19,7 @@ import java.util.List;
public class NetherSpirit implements StaffAttackHandler {
@Override
public void handle(ItemAttackMetadata attackMeta, NBTItem nbt, double range) {
public void handle(ItemAttackMetadata attackMeta, NBTItem nbt, EquipmentSlot slot, double range) {
new BukkitRunnable() {
final Vector vec = attackMeta.getPlayer().getEyeLocation().getDirection().multiply(.3);
final Location loc = attackMeta.getPlayer().getEyeLocation();

View File

@ -1,22 +1,23 @@
package net.Indyuce.mmoitems.api.interaction.weapon.untargeted.staff;
import io.lumine.mythic.lib.api.item.NBTItem;
import io.lumine.mythic.lib.api.player.EquipmentSlot;
import net.Indyuce.mmoitems.api.ItemAttackMetadata;
import org.bukkit.Location;
import java.util.Random;
public interface StaffAttackHandler {
static final Random random = new Random();
Random RANDOM = new Random();
void handle(ItemAttackMetadata attackMeta, NBTItem nbt, double range);
void handle(ItemAttackMetadata attackMeta, NBTItem nbt, EquipmentSlot slot, double range);
default Location getGround(Location loc) {
for (int j = 0; j < 20; j++) {
if (loc.getBlock().getType().isSolid())
return loc;
loc.add(0, -1, 0);
}
return loc;
}
default Location getGround(Location loc) {
for (int j = 0; j < 20; j++) {
if (loc.getBlock().getType().isSolid())
return loc;
loc.add(0, -1, 0);
}
return loc;
}
}

View File

@ -1,8 +1,8 @@
package net.Indyuce.mmoitems.api.interaction.weapon.untargeted.staff;
import io.lumine.mythic.lib.api.item.NBTItem;
import io.lumine.mythic.lib.api.player.EquipmentSlot;
import io.lumine.mythic.lib.comp.target.InteractionType;
import io.lumine.mythic.lib.damage.AttackMetadata;
import io.lumine.mythic.lib.version.VersionSound;
import net.Indyuce.mmoitems.MMOItems;
import net.Indyuce.mmoitems.MMOUtils;
@ -18,11 +18,11 @@ import org.bukkit.util.Vector;
public class SunfireSpirit implements StaffAttackHandler {
@Override
public void handle(ItemAttackMetadata attackMeta, NBTItem nbt, double range) {
public void handle(ItemAttackMetadata attackMeta, NBTItem nbt, EquipmentSlot slot, double range) {
attackMeta.getPlayer().getWorld().playSound(attackMeta.getPlayer().getLocation(), Sound.ENTITY_WITHER_SHOOT, 2, 2);
new BukkitRunnable() {
final Location target = getGround(attackMeta.getPlayer().getTargetBlock(null, (int) range * 2).getLocation()).add(0, 1.2, 0);
final double a = random.nextDouble() * Math.PI * 2;
final double a = RANDOM.nextDouble() * Math.PI * 2;
final Location loc = target.clone().add(Math.cos(a) * 4, 10, Math.sin(a) * 4);
final Vector vec = target.toVector().subtract(loc.toVector()).multiply(.015);
double ti = 0;
@ -40,7 +40,7 @@ public class SunfireSpirit implements StaffAttackHandler {
loc.getWorld().playSound(loc, VersionSound.ENTITY_FIREWORK_ROCKET_BLAST.toSound(), 2, 2);
for (Entity target : MMOUtils.getNearbyChunkEntities(loc))
if (MMOUtils.canTarget(attackMeta.getPlayer(), target, InteractionType.OFFENSE_ACTION) && target.getLocation().distanceSquared(loc) <= 9)
attackMeta.clone().applyEffectsAndDamage(nbt, (LivingEntity) target );
attackMeta.clone().applyEffectsAndDamage(nbt, (LivingEntity) target);
cancel();
break;
}

View File

@ -1,6 +1,7 @@
package net.Indyuce.mmoitems.api.interaction.weapon.untargeted.staff;
import io.lumine.mythic.lib.api.item.NBTItem;
import io.lumine.mythic.lib.api.player.EquipmentSlot;
import io.lumine.mythic.lib.comp.target.InteractionType;
import io.lumine.mythic.lib.version.VersionSound;
import net.Indyuce.mmoitems.MMOItems;
@ -17,11 +18,11 @@ import org.bukkit.util.Vector;
public class ThunderSpirit implements StaffAttackHandler {
@Override
public void handle(ItemAttackMetadata attackMeta, NBTItem nbt, double range) {
public void handle(ItemAttackMetadata attackMeta, NBTItem nbt, EquipmentSlot slot, double range) {
attackMeta.getPlayer().getWorld().playSound(attackMeta.getPlayer().getLocation(), Sound.ENTITY_WITHER_SHOOT, 2, 2);
new BukkitRunnable() {
final Location target = getGround(attackMeta.getPlayer().getTargetBlock(null, (int) range * 2).getLocation()).add(0, 1.2, 0);
final double a = random.nextDouble() * Math.PI * 2;
final double a = RANDOM.nextDouble() * Math.PI * 2;
final Location loc = target.clone().add(Math.cos(a) * 4, 10, Math.sin(a) * 4);
final Vector vec = target.toVector().subtract(loc.toVector()).multiply(.015);
double ti = 0;

View File

@ -1,9 +1,10 @@
package net.Indyuce.mmoitems.api.interaction.weapon.untargeted.staff;
import io.lumine.mythic.lib.api.item.NBTItem;
import io.lumine.mythic.lib.api.player.EquipmentSlot;
import net.Indyuce.mmoitems.MMOItems;
import net.Indyuce.mmoitems.skill.Shulker_Missile;
import net.Indyuce.mmoitems.api.ItemAttackMetadata;
import net.Indyuce.mmoitems.skill.Shulker_Missile;
import org.bukkit.Sound;
import org.bukkit.entity.EntityType;
import org.bukkit.entity.ShulkerBullet;
@ -13,7 +14,7 @@ import org.bukkit.util.Vector;
public class VoidSpirit implements StaffAttackHandler {
@Override
public void handle(ItemAttackMetadata attackMeta, NBTItem nbt, double range) {
public void handle(ItemAttackMetadata attackMeta, NBTItem nbt, EquipmentSlot slot, double range) {
Vector vec = attackMeta.getPlayer().getEyeLocation().getDirection();
attackMeta.getPlayer().getWorld().playSound(attackMeta.getPlayer().getLocation(), Sound.ENTITY_WITHER_SHOOT, 2, 2);
ShulkerBullet shulkerBullet = (ShulkerBullet) attackMeta.getPlayer().getWorld().spawnEntity(attackMeta.getPlayer().getLocation().add(0, 1, 0), EntityType.valueOf("SHULKER_BULLET"));

View File

@ -1,6 +1,7 @@
package net.Indyuce.mmoitems.api.interaction.weapon.untargeted.staff;
import io.lumine.mythic.lib.api.item.NBTItem;
import io.lumine.mythic.lib.api.player.EquipmentSlot;
import io.lumine.mythic.lib.comp.target.InteractionType;
import io.lumine.mythic.lib.util.RayTrace;
import net.Indyuce.mmoitems.MMOUtils;
@ -13,13 +14,10 @@ import org.bukkit.util.Vector;
public class XRaySpirit implements StaffAttackHandler {
@Override
public void handle(ItemAttackMetadata attackMeta, NBTItem nbt, double range) {
public void handle(ItemAttackMetadata attackMeta, NBTItem nbt, EquipmentSlot slot, double range) {
attackMeta.getPlayer().getWorld().playSound(attackMeta.getPlayer().getLocation(), Sound.BLOCK_FIRE_EXTINGUISH, 2, 2);
double a = Math.toRadians(attackMeta.getPlayer().getEyeLocation().getYaw() + 160);
Location loc = attackMeta.getPlayer().getEyeLocation().add(new Vector(Math.cos(a), 0, Math.sin(a)).multiply(.5));
RayTrace trace = new RayTrace(loc, attackMeta.getPlayer().getEyeLocation().getDirection(), range, entity -> MMOUtils.canTarget(attackMeta.getPlayer(), entity, InteractionType.OFFENSE_ACTION));
RayTrace trace = new RayTrace(attackMeta.getPlayer(), slot, range, entity -> MMOUtils.canTarget(attackMeta.getPlayer(), entity, InteractionType.OFFENSE_ACTION));
if (trace.hasHit())
attackMeta.applyEffectsAndDamage(nbt, trace.getHit());
trace.draw(2, Color.BLACK);