Files
DeltaMine/src/main/java/me/sashegdev/blackknife/LungeAttack.java
T

524 lines
28 KiB
Java

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