package me.sashegdev.blackknife; import net.kyori.adventure.text.Component; import net.kyori.adventure.text.format.NamedTextColor; import org.bukkit.Color; import org.bukkit.Location; import org.bukkit.Material; import org.bukkit.Particle; import org.bukkit.Sound; import org.bukkit.entity.BlockDisplay; import org.bukkit.entity.Display; import org.bukkit.entity.LivingEntity; import org.bukkit.entity.Player; import org.bukkit.persistence.PersistentDataType; import org.bukkit.scheduler.BukkitRunnable; import org.bukkit.util.Vector; public class LungeAttack { public static boolean tryUse(Player p, TensionManager tension, LivingEntity target) { if (target == null || !target.isValid() || target.isDead()) { p.sendActionBar(Component.text("No target! Look at entity within 120 blocks").color(NamedTextColor.RED)); return false; } if (!canSeeTarget(p, target)) { p.sendActionBar(Component.text("Target blocked by wall!").color(NamedTextColor.RED)); return false; } if (tension.getTP(p) < 225f) { p.sendActionBar(Component.text("Need 90% TP").color(NamedTextColor.RED)); return false; } tension.setTP(p, tension.getTP(p) - 225f); tension.startBattle(p, target); Location start = p.getEyeLocation().clone(); BlackKnifePlugin plugin = BlackKnifePlugin.getPlugin(BlackKnifePlugin.class); BlockDisplay catalyst = p.getWorld().spawn(start, BlockDisplay.class, d -> { d.setBlock(Material.SCULK_CATALYST.createBlockData()); d.setBrightness(new Display.Brightness(15, 15)); d.setViewRange(64); }); EntityTracker.mark(catalyst); EntityTracker.smooth(catalyst); p.getWorld().playSound(start, Sound.BLOCK_SCULK_CATALYST_BLOOM, 1f, 0.7f); double catSpeed = 3.0; new BukkitRunnable() { @Override public void run() { EntityTracker.untrack(catalyst); if (!target.isValid() || target.isDead()) { if (catalyst.isValid()) catalyst.remove(); cancel(); return; } if (!catalyst.isValid()) { cancel(); return; } Location cur = catalyst.getLocation(); Location tpos = target.getLocation().clone().add(0, 1, 0); Vector catDir = tpos.clone().subtract(cur).toVector().normalize(); Location nl = cur.clone().add(catDir.clone().multiply(catSpeed)); catalyst.teleport(nl); nl.getWorld().spawnParticle(Particle.SCULK_SOUL, nl, 2, 0.15, 0.15, 0.15, 0.02); nl.getWorld().spawnParticle(Particle.DUST, nl, 1, 0, 0, 0, 0, new Particle.DustOptions(Color.fromRGB(20, 60, 80), 1f)); if (nl.distanceSquared(tpos) < 4) { catalyst.teleport(tpos); EntityTracker.untrack(catalyst); catalyst.remove(); cancel(); tpos.getWorld().spawnParticle(Particle.EXPLOSION, tpos, 1); tpos.getWorld().playSound(tpos, Sound.BLOCK_SCULK_CATALYST_BLOOM, 1f, 0.5f); tpos.getWorld().playSound(tpos, Sound.ENTITY_WARDEN_SONIC_BOOM, 1f, 0.6f); startPhase1(p, tpos, target, plugin); } EntityTracker.untrack(catalyst); if (catalyst.getTicksLived() > 80) { catalyst.remove(); cancel(); } } }.runTaskTimer(plugin, 1L, 1L); return true; } private static boolean canSeeTarget(Player p, LivingEntity target) { if (!p.hasLineOfSight(target)) return false; var ray = p.getWorld().rayTraceBlocks(p.getEyeLocation(), p.getEyeLocation().getDirection().normalize(), 120); if (ray != null && ray.getHitBlock() != null) { double blockDist = ray.getHitBlock().getLocation().distanceSquared(p.getEyeLocation()); double entDist = target.getLocation().add(0, 1, 0).distanceSquared(p.getEyeLocation()); if (blockDist + 1 < entDist) return false; } return true; } public static boolean trySnipe(Player p, TensionManager tension, LivingEntity target) { if (target == null || !target.isValid() || target.isDead()) { p.sendActionBar(Component.text("No target! Look at entity within 120 blocks").color(NamedTextColor.RED)); return false; } if (!canSeeTarget(p, target)) { p.sendActionBar(Component.text("Target blocked by wall!").color(NamedTextColor.RED)); return false; } if (tension.getTP(p) < 62.5f) { p.sendActionBar(Component.text("Need 25% TP").color(NamedTextColor.RED)); return false; } tension.setTP(p, tension.getTP(p) - 62.5f); tension.startBattle(p, target); spawnLungeSword(p, target, 20); return true; } public static boolean tryTri(Player p, TensionManager tension, LivingEntity target) { if (target == null || !target.isValid() || target.isDead()) { p.sendActionBar(Component.text("No target! Look at entity within 120 blocks").color(NamedTextColor.RED)); return false; } if (!canSeeTarget(p, target)) { p.sendActionBar(Component.text("Target blocked by wall!").color(NamedTextColor.RED)); return false; } if (tension.getTP(p) < 87.5f) { p.sendActionBar(Component.text("Need 35% TP").color(NamedTextColor.RED)); return false; } tension.setTP(p, tension.getTP(p) - 87.5f); tension.startBattle(p, target); BlackKnifePlugin plugin = BlackKnifePlugin.getPlugin(BlackKnifePlugin.class); double baseAngle = Math.random() * 360; for (int i = 0; i < 3; i++) { int idx = i; plugin.getServer().getScheduler().runTaskLater(plugin, () -> { if (!target.isValid() || target.isDead()) return; double angle = (baseAngle + idx * 120) % 360 * Math.PI / 180; spawnLungeSwordAt(p, target, angle, 7 + Math.random() * 2, 20); if (idx == 0) p.getWorld().playSound(p.getLocation(), Sound.ENTITY_PLAYER_ATTACK_SWEEP, 1f, 0.6f); }, idx * 12L); } return true; } private static void spawnLungeSword(Player p, LivingEntity target, double damage) { double angle = Math.random() * Math.PI * 2; spawnLungeSwordAt(p, target, angle, 7 + Math.random() * 2, damage); p.getWorld().playSound(p.getLocation(), Sound.ENTITY_PLAYER_ATTACK_SWEEP, 1f, 0.7f); } private static void spawnLungeSwordAt(Player p, LivingEntity target, double angleRad, double r, double damage) { BlackKnifePlugin plugin = BlackKnifePlugin.getPlugin(BlackKnifePlugin.class); Location curTargetPos = target.getLocation().clone().add(0, 1, 0); Vector offset = new Vector(Math.cos(angleRad), 0, Math.sin(angleRad)); Location spawn = curTargetPos.clone().add(offset.clone().multiply(r)).add(0, (Math.random() - 0.5) * 2, 0); Vector aim = curTargetPos.clone().subtract(spawn).toVector().normalize().multiply(3.0); BlockDisplay sword = spawn.getWorld().spawn(spawn, BlockDisplay.class, d -> { d.setBlock(Material.DEEPSLATE_TILES.createBlockData()); d.setBrightness(new Display.Brightness(15, 15)); d.setViewRange(64); d.setTransformation(new org.bukkit.util.Transformation( new org.joml.Vector3f(-0.75f, -0.4f, -0.75f), new org.joml.AxisAngle4f(), new org.joml.Vector3f(1.5f, 1.5f, 1.5f), new org.joml.AxisAngle4f())); }); EntityTracker.mark(sword); EntityTracker.smooth(sword); new BukkitRunnable() { long hold = 5; boolean launched = false; @Override public void run() { if (!sword.isValid()) { cancel(); return; } Location cur = sword.getLocation(); if (!launched) { cur.getWorld().spawnParticle(Particle.DUST, cur.clone().add(0, 0.5, 0), 3, 0.15, 0.15, 0.15, 0, new Particle.DustOptions(Color.fromRGB(10, 30, 50), 1.5f)); cur.getWorld().spawnParticle(Particle.SCULK_SOUL, cur, 1, 0.1, 0.1, 0.1, 0); hold--; if (hold <= 0) launched = true; return; } Location nl = cur.clone().add(aim); int points = Math.max(1, (int) (aim.length() * 3)); for (int j = 0; j <= points; j++) { Location pp = cur.clone().add(aim.clone().multiply((double) j / points)); pp.getWorld().spawnParticle(Particle.DUST, pp, 1, 0, 0, 0, 0, new Particle.DustOptions(Color.fromRGB(0, 0, 0), 2.0f)); pp.getWorld().spawnParticle(Particle.SCULK_SOUL, pp, 1, 0.05, 0.05, 0.05, 0); } int steps = Math.max(1, (int) Math.ceil(aim.length() / 0.5)); for (int s = 0; s <= steps; s++) { Location check = cur.clone().add(aim.clone().multiply((double) s / steps)); EntityTracker.untrack(sword); if (check.getBlock().isSolid()) { sword.remove(); cancel(); return; } } sword.teleport(nl); for (LivingEntity le : nl.getWorld().getNearbyLivingEntities(nl, 1.0)) { if (le == p) continue; applyLungeDamage(le, p, damage); le.getWorld().spawnParticle(Particle.CRIT, le.getLocation().add(0, 1, 0), 10, 0.4, 0.4, 0.4, 0.1); EntityTracker.untrack(sword); sword.remove(); cancel(); return; } for (Player pl : nl.getWorld().getNearbyPlayers(nl, 0.9)) { if (pl == p) continue; double d2 = pl.getLocation().add(0, 1, 0).distanceSquared(nl); if (d2 > 0.25 && d2 < 0.81) plugin.getTension().tryGiveDodge(pl); } EntityTracker.untrack(sword); if (sword.getTicksLived() > 80) { sword.remove(); cancel(); } } }.runTaskTimer(plugin, 1L, 1L); } public static boolean tryOverhead(Player p, TensionManager tension, Location gazePoint) { if (gazePoint == null) { p.sendActionBar(Component.text("Look at ground within 120 blocks!").color(NamedTextColor.RED)); return false; } if (tension.getTP(p) < 62.5f) { p.sendActionBar(Component.text("Need 25% TP").color(NamedTextColor.RED)); return false; } tension.setTP(p, tension.getTP(p) - 62.5f); BlackKnifePlugin plugin = BlackKnifePlugin.getPlugin(BlackKnifePlugin.class); Location above = gazePoint.clone().add(0, 10, 0); BlockDisplay sword = p.getWorld().spawn(above, BlockDisplay.class, d -> { d.setBlock(Material.DEEPSLATE_TILES.createBlockData()); d.setBrightness(new Display.Brightness(15, 15)); d.setViewRange(64); d.setTransformation(new org.bukkit.util.Transformation( new org.joml.Vector3f(-0.75f, -0.4f, -0.75f), new org.joml.AxisAngle4f(), new org.joml.Vector3f(1.5f, 1.5f, 1.5f), new org.joml.AxisAngle4f())); }); EntityTracker.mark(sword); EntityTracker.smooth(sword); p.getWorld().playSound(gazePoint, Sound.ENTITY_PLAYER_ATTACK_SWEEP, 1f, 0.5f); p.getWorld().playSound(gazePoint, Sound.BLOCK_ANVIL_LAND, 0.6f, 1.4f); Vector vel = new Vector(0, -1.2, 0); new BukkitRunnable() { int hold = 8; @Override public void run() { if (!sword.isValid()) { cancel(); return; } Location cur = sword.getLocation(); if (hold > 0) { hold--; cur.getWorld().spawnParticle(Particle.SMOKE, cur, 2, 0.15, 0.15, 0.15, 0); cur.getWorld().spawnParticle(Particle.SCULK_SOUL, cur, 1, 0.05, 0.05, 0.05, 0); cur.getWorld().spawnParticle(Particle.ASH, cur, 1, 0, 0, 0, 0); return; } Location nl = cur.clone().add(vel); sword.teleport(nl); nl.getWorld().spawnParticle(Particle.DUST, nl, 2, 0.15, 0.15, 0.15, 0, new Particle.DustOptions(Color.fromRGB(0, 0, 0), 1.5f)); nl.getWorld().spawnParticle(Particle.SCULK_SOUL, nl, 1, 0.05, 0.05, 0.05, 0); if (nl.getY() <= gazePoint.getY() + 0.5 || nl.getBlock().isSolid()) { Location expl = gazePoint.clone(); EntityTracker.untrack(sword); sword.remove(); cancel(); expl.getWorld().spawnParticle(Particle.EXPLOSION, expl, 1); expl.getWorld().spawnParticle(Particle.SMOKE, expl, 10, 0.3, 0.1, 0.3, 0.02); for (int k = 0; k < 16; k++) { double a = (double) k / 16 * Math.PI * 2; Location r = expl.clone().add(Math.cos(a) * 2, 0.1, Math.sin(a) * 2); r.getWorld().spawnParticle(Particle.ASH, r, 1, 0, 0, 0, 0); r.getWorld().spawnParticle(Particle.SOUL, r, 1, 0.02, 0.02, 0.02, 0); } for (LivingEntity le : expl.getWorld().getNearbyLivingEntities(expl, 2.2)) { if (le == p) continue; double d = le.getLocation().distanceSquared(expl); if (d > 4.84) continue; applyLungeDamage(le, p, 20); le.getWorld().spawnParticle(Particle.CRIT, le.getLocation().add(0, 1, 0), 10, 0.4, 0.4, 0.4, 0.1); } for (Player pl : expl.getWorld().getNearbyPlayers(expl, 2.5)) { if (pl == p) continue; double d2 = pl.getLocation().add(0, 1, 0).distanceSquared(expl); if (d2 > 0.25 && d2 < 6.5) plugin.getTension().tryGiveDodge(pl); } expl.getWorld().playSound(expl, Sound.ENTITY_GENERIC_EXPLODE, 0.8f, 1.2f); return; } for (LivingEntity le : nl.getWorld().getNearbyLivingEntities(nl, 1.0)) { if (le == p) continue; applyLungeDamage(le, p, 20); EntityTracker.untrack(sword); sword.remove(); cancel(); return; } EntityTracker.untrack(sword); if (sword.getTicksLived() > 60) { sword.remove(); cancel(); } } }.runTaskTimer(plugin, 1L, 1L); return true; } private static void applyLungeDamage(LivingEntity ent, Player attacker, double amount) { if (!DamageUtil.canHurt(attacker, ent)) return; if (ent instanceof Player pl && pl.isBlocking()) amount /= 2.5; ent.getPersistentDataContainer().set(SwoonHandler.MAGIC_DAMAGE, PersistentDataType.BYTE, (byte) 1); ent.damage(amount, attacker); ent.setNoDamageTicks(0); } private static void startPhase1(Player p, Location epicenter, LivingEntity target, BlackKnifePlugin plugin) { epicenter = epicenter.clone(); Location ground = epicenter.clone(); ground.setY(epicenter.getWorld().getHighestBlockYAt(epicenter) + 1); if (ground.getY() > epicenter.getY() + 0.5) ground = epicenter.clone(); Location finalEpicenter = ground.clone(); new BukkitRunnable() { int wave = 0; @Override public void run() { if (wave >= 5) { cancel(); startPhase2(p, finalEpicenter, target, plugin); return; } spawnShockwave(p, finalEpicenter.clone(), 20, plugin); wave++; } }.runTaskTimer(plugin, 0L, 10L); } private static void spawnShockwave(Player p, Location center, double damage, BlackKnifePlugin plugin) { center.getWorld().playSound(center, Sound.BLOCK_SCULK_SHRIEKER_SHRIEK, 1f, 0.6f); double maxRadius = 40; double expandSpeed = 2.0; new BukkitRunnable() { double radius = 1; @Override public void run() { if (radius > maxRadius) { cancel(); return; } int points = (int) (radius * 6); for (int i = 0; i < points; i++) { double angle = (double) i / points * Math.PI * 2; double x = Math.cos(angle) * radius; double z = Math.sin(angle) * radius; Location ring = center.clone().add(x, 0.3, z); ring.getWorld().spawnParticle(Particle.ASH, ring, 1, 0, 0, 0, 0); ring.getWorld().spawnParticle(Particle.SOUL, ring, 1, 0.05, 0.05, 0.05, 0); ring.getWorld().spawnParticle(Particle.SCULK_SOUL, ring, 1, 0.05, 0.05, 0.05, 0); } if (radius > 3 && radius < maxRadius - 1) { double thinRadius = radius; for (LivingEntity le : center.getWorld().getNearbyLivingEntities(center, maxRadius + 2)) { if (le == p) continue; if (le.getLocation().getY() > center.getY() + 2.5) continue; double dist = Math.sqrt(Math.pow(le.getLocation().getX() - center.getX(), 2) + Math.pow(le.getLocation().getZ() - center.getZ(), 2)); if (Math.abs(dist - thinRadius) < 1.2) { applyLungeDamage(le, p, damage); le.getWorld().spawnParticle(Particle.CRIT, le.getLocation().add(0, 1, 0), 8, 0.3, 0.3, 0.3, 0.1); } } } radius += expandSpeed; } }.runTaskTimer(plugin, 1L, 1L); } private static void startPhase2(Player p, Location epicenter, LivingEntity target, BlackKnifePlugin plugin) { plugin.getServer().getScheduler().runTaskLater(plugin, () -> { new BukkitRunnable() { int idx = 0; @Override public void run() { if (idx >= 8) { cancel(); return; } Location above = epicenter.clone().add((Math.random() - 0.5) * 10, 10 + Math.random() * 3, (Math.random() - 0.5) * 10); spawnFallingSword(p, above, epicenter, 10, 0.7, plugin); idx++; } }.runTaskTimer(plugin, 1L, 3L); spawnShockwave(p, epicenter.clone(), 20, plugin); plugin.getServer().getScheduler().runTaskLater(plugin, () -> spawnShockwave(p, epicenter.clone(), 20, plugin), 12L); plugin.getServer().getScheduler().runTaskLater(plugin, () -> spawnShockwave(p, epicenter.clone(), 20, plugin), 24L); plugin.getServer().getScheduler().runTaskLater(plugin, () -> startPhase3(p, epicenter, target, plugin), 36L); }, 10L); } private static void spawnFallingSword(Player p, Location spawn, Location target, double damage, double fallSpeed, BlackKnifePlugin plugin) { BlockDisplay sword = p.getWorld().spawn(spawn, BlockDisplay.class, d -> { d.setBlock(Material.DEEPSLATE_TILES.createBlockData()); d.setBrightness(new Display.Brightness(15, 15)); d.setViewRange(64); d.setTransformation(new org.bukkit.util.Transformation( new org.joml.Vector3f(-0.35f, -0.18f, -0.35f), new org.joml.AxisAngle4f(), new org.joml.Vector3f(0.7f, 0.7f, 0.7f), new org.joml.AxisAngle4f())); }); EntityTracker.mark(sword); EntityTracker.smooth(sword); Vector vel = new Vector(0, -fallSpeed, 0); p.getWorld().playSound(spawn, Sound.ENTITY_PLAYER_ATTACK_SWEEP, 0.6f, 0.7f); new BukkitRunnable() { @Override public void run() { if (!sword.isValid()) { cancel(); return; } Location cur = sword.getLocation(); Location nl = cur.clone().add(vel); sword.teleport(nl); nl.getWorld().spawnParticle(Particle.DUST, nl, 1, 0, 0, 0, 0, new Particle.DustOptions(Color.fromRGB(0, 0, 0), 1.0f)); nl.getWorld().spawnParticle(Particle.SCULK_SOUL, nl, 1, 0.05, 0.05, 0.05, 0); if (nl.getY() <= target.getY() + 0.5 || nl.getBlock().isSolid()) { Location expl = nl.clone(); EntityTracker.untrack(sword); sword.remove(); cancel(); expl.getWorld().spawnParticle(Particle.SMOKE, expl, 6, 0.2, 0.1, 0.2, 0.02); expl.getWorld().spawnParticle(Particle.SCULK_SOUL, expl, 4, 0.2, 0.1, 0.2, 0.02); for (int k = 0; k < 12; k++) { double a = (double) k / 12 * Math.PI * 2; Location r = expl.clone().add(Math.cos(a) * 1.5, 0.1, Math.sin(a) * 1.5); r.getWorld().spawnParticle(Particle.ASH, r, 1, 0, 0, 0, 0); r.getWorld().spawnParticle(Particle.SOUL, r, 1, 0.02, 0.02, 0.02, 0); } for (LivingEntity le : expl.getWorld().getNearbyLivingEntities(expl, 1.5)) { if (le == p) continue; applyLungeDamage(le, p, damage); le.getWorld().spawnParticle(Particle.CRIT, le.getLocation().add(0, 1, 0), 6, 0.3, 0.3, 0.3, 0.1); } for (Player pl : expl.getWorld().getNearbyPlayers(expl, 0.9)) { if (pl == p) continue; double d2 = pl.getLocation().add(0, 1, 0).distanceSquared(expl); if (d2 > 0.25 && d2 < 0.81) plugin.getTension().tryGiveDodge(pl); } return; } for (LivingEntity le : nl.getWorld().getNearbyLivingEntities(nl, 0.7)) { if (le == p) continue; applyLungeDamage(le, p, damage); le.getWorld().spawnParticle(Particle.CRIT, le.getLocation().add(0, 1, 0), 8, 0.3, 0.3, 0.3, 0.1); EntityTracker.untrack(sword); sword.remove(); cancel(); return; } EntityTracker.untrack(sword); if (sword.getTicksLived() > 80) { sword.remove(); cancel(); } } }.runTaskTimer(plugin, 1L, 1L); } private static void startPhase3(Player p, Location epicenter, LivingEntity target, BlackKnifePlugin plugin) { new BukkitRunnable() { int idx = 0; @Override public void run() { if (idx >= 20) { cancel(); startPhase4(p, target, plugin); return; } Location above = epicenter.clone().add((Math.random() - 0.5) * 12, 10 + Math.random() * 4, (Math.random() - 0.5) * 12); spawnFallingSword(p, above, epicenter, 10, 0.7, plugin); idx++; } }.runTaskTimer(plugin, 2L, 4L); } private static void startPhase4(Player p, LivingEntity target, BlackKnifePlugin plugin) { plugin.getServer().getScheduler().runTaskLater(plugin, () -> { new BukkitRunnable() { int swordIdx = 0; long nextDelay = 30; long delayCounter = 0; @Override public void run() { if (swordIdx >= 30 || !target.isValid() || target.isDead()) { cancel(); return; } if (delayCounter < nextDelay) { delayCounter++; return; } delayCounter = 0; double frac = (double) swordIdx / 29; long holdTicks = Math.round(15 - frac * 10); long gapTicks = Math.round(20 - frac * 5); nextDelay = gapTicks; double angle = (swordIdx * 137.5) % 360 * Math.PI / 180; double r = 7 + Math.random() * 3; Vector offset = new Vector(Math.cos(angle), 0, Math.sin(angle)); Location curTargetPos = target.getLocation().clone().add(0, 1, 0); Location spawn = curTargetPos.clone().add(offset.clone().multiply(r)).add(0, (Math.random() - 0.5) * 2, 0); Vector aim = curTargetPos.clone().subtract(spawn).toVector().normalize().multiply(3.0); BlockDisplay sword = spawn.getWorld().spawn(spawn, BlockDisplay.class, d -> { d.setBlock(Material.DEEPSLATE_TILES.createBlockData()); d.setBrightness(new Display.Brightness(15, 15)); d.setViewRange(64); d.setTransformation(new org.bukkit.util.Transformation( new org.joml.Vector3f(-0.75f, -0.4f, -0.75f), new org.joml.AxisAngle4f(), new org.joml.Vector3f(1.5f, 1.5f, 1.5f), new org.joml.AxisAngle4f())); }); EntityTracker.mark(sword); EntityTracker.smooth(sword); spawn.getWorld().playSound(spawn, Sound.ENTITY_PLAYER_ATTACK_SWEEP, 0.8f, 0.5f); new BukkitRunnable() { long hold = holdTicks; boolean launched = false; @Override public void run() { if (!sword.isValid()) { cancel(); return; } Location cur = sword.getLocation(); if (!launched) { cur.getWorld().spawnParticle(Particle.DUST, cur.clone().add(0, 0.5, 0), 3, 0.15, 0.15, 0.15, 0, new Particle.DustOptions(Color.fromRGB(10, 30, 50), 1.5f)); cur.getWorld().spawnParticle(Particle.SCULK_SOUL, cur, 1, 0.1, 0.1, 0.1, 0); hold--; if (hold <= 0) launched = true; return; } Location nl = cur.clone().add(aim); int points = Math.max(1, (int) (aim.length() * 3)); Vector step = aim.clone(); for (int j = 0; j <= points; j++) { Location pp = cur.clone().add(step.clone().multiply((double) j / points)); pp.getWorld().spawnParticle(Particle.DUST, pp, 1, 0, 0, 0, 0, new Particle.DustOptions(Color.fromRGB(0, 0, 0), 2.0f)); pp.getWorld().spawnParticle(Particle.SCULK_SOUL, pp, 1, 0.05, 0.05, 0.05, 0); } int steps = Math.max(1, (int) Math.ceil(aim.length() / 0.5)); for (int s = 0; s <= steps; s++) { Location check = cur.clone().add(aim.clone().multiply((double) s / steps)); EntityTracker.untrack(sword); if (check.getBlock().isSolid()) { sword.remove(); cancel(); return; } } sword.teleport(nl); for (LivingEntity le : nl.getWorld().getNearbyLivingEntities(nl, 1.0)) { if (le == p) continue; applyLungeDamage(le, p, 20); le.getWorld().spawnParticle(Particle.CRIT, le.getLocation().add(0, 1, 0), 10, 0.4, 0.4, 0.4, 0.1); EntityTracker.untrack(sword); sword.remove(); cancel(); return; } for (Player pl : nl.getWorld().getNearbyPlayers(nl, 0.9)) { if (pl == p) continue; double d2 = pl.getLocation().add(0, 1, 0).distanceSquared(nl); if (d2 > 0.25 && d2 < 0.81) plugin.getTension().tryGiveDodge(pl); } EntityTracker.untrack(sword); if (sword.getTicksLived() > 80) { sword.remove(); cancel(); } } }.runTaskTimer(plugin, 1L, 1L); swordIdx++; } }.runTaskTimer(plugin, 1L, 1L); }, 10L); } }