package me.sashegdev.blackknife; import org.bukkit.Color; import org.bukkit.Location; import org.bukkit.Particle; import org.bukkit.Sound; import org.bukkit.entity.LivingEntity; import org.bukkit.entity.Marker; import org.bukkit.entity.Player; import org.bukkit.persistence.PersistentDataType; import org.bukkit.scheduler.BukkitRunnable; import org.bukkit.util.Vector; public class SwordAttack { public static boolean tryUse(Player p, TensionManager tension) { if (tension.getTP(p) < 12.5f) { p.sendActionBar(net.kyori.adventure.text.Component.text("Need 5% TP").color(net.kyori.adventure.text.format.NamedTextColor.RED)); return false; } tension.setTP(p, tension.getTP(p) - 12.5f); launch(p, tension, p.getEyeLocation().getDirection().normalize(), 2.0); return true; } public static void launch(Player p, TensionManager tension, Vector dir, double speed) { dir = dir.clone().normalize(); Location spawn = p.getLocation().add(0, 2.2, 0).add(dir.clone().multiply(1)); Marker marker = p.getWorld().spawn(spawn, Marker.class, m -> m.setPersistent(false)); EntityTracker.mark(marker); p.getWorld().playSound(spawn, Sound.ENTITY_PLAYER_ATTACK_SWEEP, 1f, 1.2f); BlackKnifePlugin plugin = BlackKnifePlugin.getPlugin(BlackKnifePlugin.class); Vector initVel = dir.clone().multiply(speed); new BukkitRunnable() { Vector vel = initVel.clone(); int homingTicks = 30; @Override public void run() { if (!marker.isValid() || marker.isDead()) { cancel(); return; } if (homingTicks > 0) { Vector gaze = p.getEyeLocation().getDirection().normalize().multiply(speed); vel = vel.multiply(0.82).add(gaze.multiply(0.18)).normalize().multiply(speed); homingTicks--; } Location cur = marker.getLocation(); Location nl = cur.clone().add(vel); Vector step = nl.clone().subtract(cur).toVector(); double len = step.length(); int steps = Math.max(1, (int) Math.ceil(len / 0.5)); for (int s = 0; s <= steps; s++) { Location check = cur.clone().add(step.clone().multiply((double) s / steps)); if (check.getBlock().isSolid()) { Location expl = cur.clone().add(step.clone().multiply(Math.max(0, (double) (s - 1) / steps))); marker.teleport(expl); for (int k = 0; k < 12; k++) { double a = (double) k / 12 * Math.PI * 2; for (int h = -1; h <= 1; h++) { Location sphere = expl.clone().add(Math.cos(a) * 0.5, h * 0.25, Math.sin(a) * 0.5); sphere.getWorld().spawnParticle(Particle.SMOKE, sphere, 1, 0.02, 0.02, 0.02, 0); sphere.getWorld().spawnParticle(Particle.DUST, sphere, 1, 0, 0, 0, 0, new Particle.DustOptions(Color.fromRGB(30, 30, 30), 0.9f)); } } expl.getWorld().spawnParticle(Particle.CRIT, expl, 6, 0.2, 0.2, 0.2, 0.05); expl.getWorld().playSound(expl, Sound.BLOCK_STONE_BREAK, 1f, 0.8f); EntityTracker.untrack(marker); marker.remove(); cancel(); return; } } int points = Math.max(1, (int) (len * 4)); for (int i = 0; i <= points; i++) { Location pp = cur.clone().add(step.clone().multiply((double) i / points)); pp.getWorld().spawnParticle(Particle.DUST, pp, 1, 0, 0, 0, 0, new Particle.DustOptions(Color.fromRGB(0, 0, 0), 1.5f)); } marker.teleport(nl); for (var ent : nl.getWorld().getNearbyEntities(nl, 0.7, 0.7, 0.7, e -> e instanceof LivingEntity && e != p)) { LivingEntity le = (LivingEntity) ent; DamageUtil.apply(p, le, 15.0); Location hit = le.getLocation().add(0, 1, 0); Particles.ring(hit.getWorld(), hit, 0.5, Particles.dust(40, 40, 40, 0.9f), 12, 3); hit.getWorld().playSound(hit, Sound.ENTITY_PLAYER_ATTACK_CRIT, 1f, 0.9f); le.addPotionEffect(new org.bukkit.potion.PotionEffect(org.bukkit.potion.PotionEffectType.SLOWNESS, 40, 1, false, false, true)); le.addPotionEffect(new org.bukkit.potion.PotionEffect(org.bukkit.potion.PotionEffectType.WEAKNESS, 30, 0, false, false, true)); EntityTracker.untrack(marker); marker.remove(); cancel(); return; } for (Player pl : nl.getWorld().getNearbyPlayers(nl, 0.9)) { if (pl == p) continue; double d = pl.getLocation().add(0, 1, 0).distanceSquared(nl); if (d > 0.25 && d < 0.81) { BlackKnifePlugin.getPlugin(BlackKnifePlugin.class).getTension().tryGiveDodge(pl); pl.getWorld().spawnParticle(Particle.DUST, pl.getLocation().add(0, 0.25, 0), 25, 0.5, 0.05, 0.5, 0, new Particle.DustOptions(Color.fromRGB(255, 255, 255), 0.15f)); } } EntityTracker.untrack(marker); if (nl.distanceSquared(p.getEyeLocation()) > 14400) { marker.remove(); cancel(); return; } EntityTracker.untrack(marker); if (marker.getTicksLived() > 80) { marker.remove(); cancel(); } } }.runTaskTimer(plugin, 1L, 1L); } }