524 lines
28 KiB
Java
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);
|
|
}
|
|
}
|