feat: GRENADE parabolic nade (35/5.5, 25TP, 15cd) + DIVE sky strike (25dmg knockback, 87.5TP, 120cd)
This commit is contained in:
@@ -25,7 +25,7 @@ public class BlackKnifeListener implements Listener {
|
||||
private final java.util.Map<java.util.UUID, BukkitRunnable> previewTasks = new java.util.HashMap<>();
|
||||
private final java.util.Map<java.util.UUID, BukkitRunnable> lungeHighlightTasks = new java.util.HashMap<>();
|
||||
private final java.util.Map<java.util.UUID, Integer> swordCombo = new java.util.HashMap<>();
|
||||
private static final String[] MODES = {"STARS", "SWORD", "DIAMOND", "DIAMOND_TRI", "BARRAGE", "LUNGE", "SNIPE", "TRI", "OVERHEAD"};
|
||||
private static final String[] MODES = {"STARS", "SWORD", "DIAMOND", "DIAMOND_TRI", "BARRAGE", "LUNGE", "SNIPE", "TRI", "OVERHEAD", "GRENADE", "DIVE"};
|
||||
|
||||
public BlackKnifeListener(BlackKnifePlugin plugin) { this.plugin = plugin; }
|
||||
|
||||
@@ -155,6 +155,15 @@ public class BlackKnifeListener implements Listener {
|
||||
if (LungeAttack.tryOverhead(p, tm, gaze)) {
|
||||
p.setCooldown(item.getType(), 50);
|
||||
}
|
||||
} else if (m.equals("GRENADE")) {
|
||||
if (GrenadeAttack.tryUse(p, tm)) {
|
||||
p.setCooldown(item.getType(), 15);
|
||||
}
|
||||
} else if (m.equals("DIVE")) {
|
||||
if (SkyDiveAttack.tryUse(p, tm)) {
|
||||
p.setCooldown(item.getType(), 120);
|
||||
clearHighlight(p);
|
||||
}
|
||||
}
|
||||
e.setCancelled(true);
|
||||
}
|
||||
@@ -269,6 +278,44 @@ public class BlackKnifeListener implements Listener {
|
||||
};
|
||||
r.runTaskTimer(plugin, 1L, 4L);
|
||||
lungeHighlightTasks.put(p.getUniqueId(), r);
|
||||
} else if (m.equals("GRENADE")) {
|
||||
BukkitRunnable r = new BukkitRunnable() {
|
||||
@Override public void run() {
|
||||
if (!p.isOnline() || !getMode(p).equals("GRENADE")) { cancel(); return; }
|
||||
if (p.hasCooldown(Material.NETHERITE_SWORD)) return;
|
||||
if (!BlackKnifeItem.is(p.getInventory().getItemInMainHand()) && !BlackKnifeItem.is(p.getInventory().getItemInOffHand())) return;
|
||||
Location gp = getGazePoint(p);
|
||||
for (int i = 0; i < 16; i++) {
|
||||
double a = (double) i / 16 * Math.PI * 2;
|
||||
Location ring = gp.clone().add(Math.cos(a) * 1.5, 0.1, Math.sin(a) * 1.5);
|
||||
p.spawnParticle(Particle.DUST, ring, 1, 0, 0, 0, 0, new Particle.DustOptions(Color.fromRGB(255, 40, 40), 1.2f));
|
||||
}
|
||||
for (int i = 0; i < 24; i++) {
|
||||
double a = (double) i / 24 * Math.PI * 2;
|
||||
Location ring = gp.clone().add(Math.cos(a) * 3.0, 0.1, Math.sin(a) * 3.0);
|
||||
p.spawnParticle(Particle.DUST, ring, 1, 0, 0, 0, 0, new Particle.DustOptions(Color.fromRGB(255, 200, 60), 1.0f));
|
||||
}
|
||||
}
|
||||
};
|
||||
r.runTaskTimer(plugin, 1L, 4L);
|
||||
lungeHighlightTasks.put(p.getUniqueId(), r);
|
||||
} else if (m.equals("DIVE")) {
|
||||
BukkitRunnable r = new BukkitRunnable() {
|
||||
@Override public void run() {
|
||||
if (!p.isOnline() || !getMode(p).equals("DIVE")) { cancel(); return; }
|
||||
if (p.hasCooldown(Material.NETHERITE_SWORD)) return;
|
||||
if (!BlackKnifeItem.is(p.getInventory().getItemInMainHand()) && !BlackKnifeItem.is(p.getInventory().getItemInOffHand())) return;
|
||||
Location gp = getGazePoint(p);
|
||||
for (int i = 0; i < 16; i++) {
|
||||
double a = (double) i / 16 * Math.PI * 2;
|
||||
Location ring = gp.clone().add(Math.cos(a) * 1.2, 0.1, Math.sin(a) * 1.2);
|
||||
p.spawnParticle(Particle.DUST, ring, 1, 0, 0, 0, 0, new Particle.DustOptions(Color.fromRGB(80, 160, 255), 1.2f));
|
||||
}
|
||||
p.spawnParticle(Particle.CLOUD, gp.clone().add(0, 0.2, 0), 1, 0.1, 0.1, 0.1, 0);
|
||||
}
|
||||
};
|
||||
r.runTaskTimer(plugin, 1L, 4L);
|
||||
lungeHighlightTasks.put(p.getUniqueId(), r);
|
||||
} else if (m.equals("DIAMOND_TRI")) {
|
||||
BukkitRunnable r = new BukkitRunnable() {
|
||||
@Override public void run() {
|
||||
|
||||
@@ -31,6 +31,7 @@ public class BlackKnifePlugin extends JavaPlugin {
|
||||
getServer().getPluginManager().registerEvents(knifeListener, this);
|
||||
getServer().getPluginManager().registerEvents(swoonHandler, this);
|
||||
getServer().getPluginManager().registerEvents(new AttackTpListener(tension), this);
|
||||
getServer().getPluginManager().registerEvents(new SkyDiveAttack(), this);
|
||||
getServer().getPluginManager().registerEvents(new ParryHandler(this), this);
|
||||
getServer().getPluginManager().registerEvents(magicListener, this);
|
||||
getServer().getPluginManager().registerEvents(chatHandler, this);
|
||||
|
||||
@@ -25,7 +25,7 @@ public class EntityTracker {
|
||||
boolean isBk = e.getPersistentDataContainer().has(BK_MARK, PersistentDataType.BYTE) || tracked.contains(e.getUniqueId());
|
||||
if (!isBk && e instanceof org.bukkit.entity.BlockDisplay bd) {
|
||||
var mat = bd.getBlock().getMaterial();
|
||||
if (mat == org.bukkit.Material.DEEPSLATE_TILES || mat == org.bukkit.Material.SCULK_CATALYST || mat == org.bukkit.Material.AMETHYST_BLOCK || mat == org.bukkit.Material.MEDIUM_AMETHYST_BUD) isBk = true;
|
||||
if (mat == org.bukkit.Material.DEEPSLATE_TILES || mat == org.bukkit.Material.SCULK_CATALYST || mat == org.bukkit.Material.AMETHYST_BLOCK || mat == org.bukkit.Material.MEDIUM_AMETHYST_BUD || mat == org.bukkit.Material.TNT) isBk = true;
|
||||
}
|
||||
if (isBk) { untrack(e); e.remove(); count++; }
|
||||
}
|
||||
|
||||
@@ -0,0 +1,211 @@
|
||||
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.scheduler.BukkitRunnable;
|
||||
import org.bukkit.util.Vector;
|
||||
|
||||
import java.util.ArrayDeque;
|
||||
import java.util.Deque;
|
||||
|
||||
public class GrenadeAttack {
|
||||
private static final float COST = 25f;
|
||||
private static final double LETHAL_RADIUS = 1.5;
|
||||
private static final double CHIP_RADIUS = 3.0;
|
||||
private static final double LETHAL_DAMAGE = 35.0;
|
||||
private static final double CHIP_DAMAGE = 5.5;
|
||||
private static final double MAX_RANGE = 30.0;
|
||||
private static final double ARC_MIN = 1.5;
|
||||
private static final double ARC_PER_BLOCK = 0.25;
|
||||
private static final double ARC_MAX = 4.0;
|
||||
|
||||
public static boolean tryUse(Player p, TensionManager tension) {
|
||||
if (SwoonHandler.isSwooned(p)) return false;
|
||||
if (tension.getTP(p) < COST) {
|
||||
p.sendActionBar(Component.text("Need 10% TP").color(NamedTextColor.RED));
|
||||
return false;
|
||||
}
|
||||
tension.setTP(p, tension.getTP(p) - COST);
|
||||
Location eye = p.getEyeLocation();
|
||||
Vector dir = eye.getDirection().normalize();
|
||||
Location target = computeTarget(p, eye, dir);
|
||||
double dist = eye.distance(target);
|
||||
int duration = (int) Math.max(10, Math.min(40, dist * 3));
|
||||
double arcH = Math.max(ARC_MIN, Math.min(ARC_MAX, dist * ARC_PER_BLOCK));
|
||||
Location spawn = eye.clone().add(dir.clone().multiply(1)).add(0, 0.2, 0);
|
||||
Vector throwDir = target.clone().subtract(spawn).toVector().normalize();
|
||||
|
||||
BlockDisplay nade = p.getWorld().spawn(spawn, BlockDisplay.class, d -> {
|
||||
d.setBlock(Material.TNT.createBlockData());
|
||||
d.setBrightness(new Display.Brightness(15, 15));
|
||||
d.setViewRange(64);
|
||||
d.setTransformation(new org.bukkit.util.Transformation(
|
||||
new org.joml.Vector3f(-0.25f, -0.25f, -0.25f),
|
||||
new org.joml.AxisAngle4f(),
|
||||
new org.joml.Vector3f(0.5f, 0.5f, 0.5f),
|
||||
new org.joml.AxisAngle4f()));
|
||||
});
|
||||
EntityTracker.mark(nade);
|
||||
p.getWorld().playSound(spawn, Sound.ENTITY_SNOWBALL_THROW, 1f, 0.7f);
|
||||
BlackKnifePlugin plugin = BlackKnifePlugin.getPlugin(BlackKnifePlugin.class);
|
||||
|
||||
new BukkitRunnable() {
|
||||
Location flightStart = spawn.clone();
|
||||
Location flightTarget = target.clone();
|
||||
Vector flightDir = throwDir.clone();
|
||||
int flightDuration = duration;
|
||||
int elapsed = 0;
|
||||
int tick = 0;
|
||||
double arc = arcH;
|
||||
boolean targetWasSolid = flightTarget.getBlock().isSolid();
|
||||
final Deque<Location> trail = new ArrayDeque<>();
|
||||
|
||||
@Override public void run() {
|
||||
if (!p.isOnline() || p.isDead() || !nade.isValid()) {
|
||||
EntityTracker.untrack(nade);
|
||||
if (nade.isValid()) nade.remove();
|
||||
cancel();
|
||||
return;
|
||||
}
|
||||
tick++;
|
||||
elapsed++;
|
||||
|
||||
if (tick % 3 == 0 && needsRetarget(nade.getLocation())) {
|
||||
retarget(nade.getLocation());
|
||||
}
|
||||
|
||||
double t = Math.min(1.0, (double) elapsed / flightDuration);
|
||||
Location base = flightStart.clone().add(flightTarget.clone().subtract(flightStart).toVector().multiply(t));
|
||||
base.add(0, Math.sin(Math.PI * t) * arc, 0);
|
||||
nade.teleport(base);
|
||||
base.getWorld().spawnParticle(Particle.SMOKE, base, 1, 0.05, 0.05, 0.05, 0);
|
||||
|
||||
trail.addLast(base.clone());
|
||||
while (trail.size() > 12) trail.pollFirst();
|
||||
renderTrail();
|
||||
|
||||
if (tick % 2 == 0) renderRings(flightTarget);
|
||||
|
||||
boolean landed = t >= 1.0 || base.getBlock().isSolid();
|
||||
if (landed || nade.getTicksLived() > 100) {
|
||||
Location expl = landed ? flightTarget.clone() : base.clone();
|
||||
EntityTracker.untrack(nade);
|
||||
nade.remove();
|
||||
cancel();
|
||||
explode(p, expl, plugin);
|
||||
}
|
||||
}
|
||||
|
||||
private boolean needsRetarget(Location cur) {
|
||||
boolean nowSolid = flightTarget.getBlock().isSolid();
|
||||
if (nowSolid != targetWasSolid) return true;
|
||||
Vector toTarget = flightTarget.clone().subtract(cur).toVector();
|
||||
double remaining = toTarget.length();
|
||||
if (remaining < 0.5) return false;
|
||||
var ray = cur.getWorld().rayTraceBlocks(cur, toTarget.normalize(), remaining);
|
||||
if (ray != null && ray.getHitBlock() != null) {
|
||||
double hitDist = ray.getHitBlock().getLocation().distance(cur);
|
||||
return hitDist + 0.5 < remaining;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
private void retarget(Location cur) {
|
||||
var ray = cur.getWorld().rayTraceBlocks(cur, flightDir, MAX_RANGE);
|
||||
Location nt;
|
||||
if (ray != null && ray.getHitPosition() != null) {
|
||||
nt = new Location(cur.getWorld(), ray.getHitPosition().getX(), ray.getHitPosition().getY(), ray.getHitPosition().getZ());
|
||||
if (ray.getHitBlockFace() != null) nt.add(0, 0.1, 0);
|
||||
} else {
|
||||
nt = cur.clone().add(flightDir.clone().multiply(12));
|
||||
nt.setY(cur.getWorld().getHighestBlockYAt(nt) + 1);
|
||||
}
|
||||
flightStart = cur.clone();
|
||||
flightTarget = nt.clone();
|
||||
double remaining = Math.max(1.0, cur.distance(nt));
|
||||
flightDuration = (int) Math.max(6, Math.min(40, remaining * 3));
|
||||
elapsed = 0;
|
||||
arc = Math.max(ARC_MIN, Math.min(ARC_MAX, remaining * ARC_PER_BLOCK));
|
||||
targetWasSolid = flightTarget.getBlock().isSolid();
|
||||
}
|
||||
|
||||
private void renderTrail() {
|
||||
int size = trail.size();
|
||||
int i = 0;
|
||||
for (Location tp : trail) {
|
||||
float frac = size <= 1 ? 1f : (float) i / (size - 1);
|
||||
float dustSize = 0.3f + frac * 1.1f;
|
||||
p.spawnParticle(Particle.DUST, tp, 1, 0.02, 0.02, 0.02, 0,
|
||||
new Particle.DustOptions(Color.fromRGB(180, 120, 40), dustSize));
|
||||
i++;
|
||||
}
|
||||
}
|
||||
|
||||
private void renderRings(Location ground) {
|
||||
Location g = ground.clone().add(0, 0.15, 0);
|
||||
for (int k = 0; k < 16; k++) {
|
||||
double a = (double) k / 16 * Math.PI * 2;
|
||||
Location ring = g.clone().add(Math.cos(a) * LETHAL_RADIUS, 0, Math.sin(a) * LETHAL_RADIUS);
|
||||
p.spawnParticle(Particle.DUST, ring, 1, 0, 0, 0, 0,
|
||||
new Particle.DustOptions(Color.fromRGB(255, 40, 40), 1.2f));
|
||||
}
|
||||
for (int k = 0; k < 24; k++) {
|
||||
double a = (double) k / 24 * Math.PI * 2;
|
||||
Location ring = g.clone().add(Math.cos(a) * CHIP_RADIUS, 0, Math.sin(a) * CHIP_RADIUS);
|
||||
p.spawnParticle(Particle.DUST, ring, 1, 0, 0, 0, 0,
|
||||
new Particle.DustOptions(Color.fromRGB(255, 200, 60), 1.0f));
|
||||
}
|
||||
}
|
||||
}.runTaskTimer(plugin, 1L, 1L);
|
||||
return true;
|
||||
}
|
||||
|
||||
private static Location computeTarget(Player p, Location eye, Vector dir) {
|
||||
var ray = p.getWorld().rayTraceBlocks(eye, dir, MAX_RANGE);
|
||||
if (ray != null && ray.getHitPosition() != null) {
|
||||
Location loc = new Location(p.getWorld(), ray.getHitPosition().getX(), ray.getHitPosition().getY(), ray.getHitPosition().getZ());
|
||||
if (ray.getHitBlockFace() != null) loc.add(0, 0.1, 0);
|
||||
return loc;
|
||||
}
|
||||
Location fallback = eye.clone().add(dir.clone().multiply(20));
|
||||
fallback.setY(p.getWorld().getHighestBlockYAt(fallback) + 1);
|
||||
return fallback;
|
||||
}
|
||||
|
||||
private static void explode(Player p, Location expl, BlackKnifePlugin plugin) {
|
||||
expl.getWorld().spawnParticle(Particle.EXPLOSION, expl, 2);
|
||||
expl.getWorld().spawnParticle(Particle.SMOKE, expl, 12, 0.3, 0.3, 0.3, 0.05);
|
||||
expl.getWorld().spawnParticle(Particle.DUST, expl, 10, 0.3, 0.2, 0.3, 0,
|
||||
new Particle.DustOptions(Color.fromRGB(255, 120, 30), 1.2f));
|
||||
expl.getWorld().playSound(expl, Sound.ENTITY_GENERIC_EXPLODE, 1f, 1.1f);
|
||||
TensionManager tm = plugin.getTension();
|
||||
for (LivingEntity le : expl.getWorld().getNearbyLivingEntities(expl, CHIP_RADIUS + 0.5)) {
|
||||
if (le == p) continue;
|
||||
if (!DamageUtil.canHurt(p, le)) continue;
|
||||
double d = le.getLocation().add(0, 1, 0).distance(expl);
|
||||
if (d > CHIP_RADIUS) continue;
|
||||
if (d <= LETHAL_RADIUS) {
|
||||
DamageUtil.apply(p, le, LETHAL_DAMAGE);
|
||||
tm.startBattle(p, le);
|
||||
} else {
|
||||
DamageUtil.apply(p, le, CHIP_DAMAGE);
|
||||
tm.startBattle(p, le);
|
||||
}
|
||||
le.getWorld().spawnParticle(Particle.CRIT, le.getLocation().add(0, 1, 0), 8, 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) tm.tryGiveDodge(pl);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -65,7 +65,7 @@ public class KillMessageHandler implements Listener {
|
||||
private boolean isKnifeMagic(String magic) {
|
||||
if (magic == null) return false;
|
||||
String m = magic.toLowerCase();
|
||||
return m.contains("swoon") || m.contains("sword") || m.contains("diamond") || m.contains("lunge") || m.contains("snipe") || m.contains("tri") || m.contains("overhead") || m.contains("stars") || m.contains("black knife") || m.contains("knife");
|
||||
return m.contains("swoon") || m.contains("sword") || m.contains("diamond") || m.contains("lunge") || m.contains("snipe") || m.contains("tri") || m.contains("overhead") || m.contains("stars") || m.contains("black knife") || m.contains("knife") || m.contains("grenade") || m.contains("dive");
|
||||
}
|
||||
|
||||
private String obf(String name) {
|
||||
|
||||
@@ -0,0 +1,220 @@
|
||||
package me.sashegdev.blackknife;
|
||||
|
||||
import net.kyori.adventure.text.Component;
|
||||
import net.kyori.adventure.text.format.NamedTextColor;
|
||||
import org.bukkit.Color;
|
||||
import org.bukkit.GameMode;
|
||||
import org.bukkit.Location;
|
||||
import org.bukkit.Particle;
|
||||
import org.bukkit.Sound;
|
||||
import org.bukkit.entity.LivingEntity;
|
||||
import org.bukkit.entity.Player;
|
||||
import org.bukkit.event.EventHandler;
|
||||
import org.bukkit.event.EventPriority;
|
||||
import org.bukkit.event.Listener;
|
||||
import org.bukkit.event.entity.EntityDamageEvent;
|
||||
import org.bukkit.event.entity.PlayerDeathEvent;
|
||||
import org.bukkit.event.player.PlayerMoveEvent;
|
||||
import org.bukkit.event.player.PlayerQuitEvent;
|
||||
import org.bukkit.scheduler.BukkitRunnable;
|
||||
import org.bukkit.util.Vector;
|
||||
|
||||
import java.util.Map;
|
||||
import java.util.UUID;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
|
||||
public class SkyDiveAttack implements Listener {
|
||||
private static final float COST = 87.5f;
|
||||
private static final double DAMAGE = 25.0;
|
||||
private static final double LAUNCH_UP = 1.3;
|
||||
private static final double LAUNCH_FORWARD = 0.4;
|
||||
private static final double DASH_SPEED = 2.8;
|
||||
private static final long FREEZE_TICKS = 30L;
|
||||
private static final long FALL_IMMUNE_MS = 8000L;
|
||||
|
||||
private static final Map<UUID, Location> frozen = new ConcurrentHashMap<>();
|
||||
private static final Map<UUID, Long> immuneUntil = new ConcurrentHashMap<>();
|
||||
private static final Map<UUID, Boolean> diving = new ConcurrentHashMap<>();
|
||||
|
||||
public static boolean tryUse(Player p, TensionManager tension) {
|
||||
if (SwoonHandler.isSwooned(p)) return false;
|
||||
if (p.getGameMode() == GameMode.CREATIVE || p.getGameMode() == GameMode.SPECTATOR) return false;
|
||||
if (Boolean.TRUE.equals(diving.get(p.getUniqueId()))) {
|
||||
p.sendActionBar(Component.text("Already diving!").color(NamedTextColor.RED));
|
||||
return false;
|
||||
}
|
||||
if (tension.getTP(p) < COST) {
|
||||
p.sendActionBar(Component.text("Need 35% TP").color(NamedTextColor.RED));
|
||||
return false;
|
||||
}
|
||||
tension.setTP(p, tension.getTP(p) - COST);
|
||||
BlackKnifePlugin plugin = BlackKnifePlugin.getPlugin(BlackKnifePlugin.class);
|
||||
|
||||
Vector look = p.getEyeLocation().getDirection();
|
||||
Vector horizontal = new Vector(look.getX(), 0, look.getZ());
|
||||
if (horizontal.lengthSquared() > 0.0001) horizontal.normalize().multiply(LAUNCH_FORWARD);
|
||||
else horizontal = new Vector(0, 0, 0);
|
||||
p.setVelocity(new Vector(horizontal.getX(), LAUNCH_UP, horizontal.getZ()));
|
||||
p.setFallDistance(0);
|
||||
immuneUntil.put(p.getUniqueId(), System.currentTimeMillis() + FALL_IMMUNE_MS);
|
||||
diving.put(p.getUniqueId(), true);
|
||||
p.getWorld().playSound(p.getLocation(), Sound.ENTITY_FIREWORK_ROCKET_LAUNCH, 1f, 0.8f);
|
||||
p.getWorld().spawnParticle(Particle.CLOUD, p.getLocation(), 10, 0.3, 0.2, 0.3, 0.05);
|
||||
|
||||
new BukkitRunnable() {
|
||||
final double startY = p.getLocation().getY();
|
||||
int ticks = 0;
|
||||
|
||||
@Override public void run() {
|
||||
if (!p.isOnline() || p.isDead()) { cleanup(p); cancel(); return; }
|
||||
p.setFallDistance(0);
|
||||
ticks++;
|
||||
double gained = p.getLocation().getY() - startY;
|
||||
Vector v = p.getVelocity();
|
||||
if (v.getY() <= 0.25 || gained >= 5.5 || ticks >= 14) {
|
||||
cancel();
|
||||
freeze(p, plugin);
|
||||
}
|
||||
if (ticks > 40) { cancel(); freeze(p, plugin); }
|
||||
}
|
||||
}.runTaskTimer(plugin, 1L, 1L);
|
||||
return true;
|
||||
}
|
||||
|
||||
private static void freeze(Player p, BlackKnifePlugin plugin) {
|
||||
if (!p.isOnline() || p.isDead()) { cleanup(p); return; }
|
||||
Location top = p.getLocation().clone();
|
||||
p.setVelocity(new Vector(0, 0, 0));
|
||||
p.setFallDistance(0);
|
||||
frozen.put(p.getUniqueId(), top);
|
||||
p.getWorld().playSound(top, Sound.BLOCK_BEACON_ACTIVATE, 0.8f, 1.4f);
|
||||
|
||||
new BukkitRunnable() {
|
||||
long left = FREEZE_TICKS;
|
||||
|
||||
@Override public void run() {
|
||||
if (!p.isOnline() || p.isDead()) { cleanup(p); cancel(); return; }
|
||||
Location anchor = frozen.get(p.getUniqueId());
|
||||
if (anchor == null) { cancel(); return; }
|
||||
Location cur = p.getLocation();
|
||||
anchor.setYaw(cur.getYaw());
|
||||
anchor.setPitch(cur.getPitch());
|
||||
p.teleport(anchor);
|
||||
p.setVelocity(new Vector(0, 0, 0));
|
||||
p.setFallDistance(0);
|
||||
for (int k = 0; k < 10; k++) {
|
||||
double a = (double) k / 10 * Math.PI * 2;
|
||||
Location ring = anchor.clone().add(Math.cos(a) * 1.2, 0.1, Math.sin(a) * 1.2);
|
||||
p.getWorld().spawnParticle(Particle.DUST, ring, 1, 0, 0, 0, 0,
|
||||
new Particle.DustOptions(Color.fromRGB(80, 160, 255), 1.0f));
|
||||
}
|
||||
left--;
|
||||
if (left <= 0) {
|
||||
frozen.remove(p.getUniqueId());
|
||||
cancel();
|
||||
dash(p, plugin);
|
||||
}
|
||||
}
|
||||
}.runTaskTimer(plugin, 1L, 1L);
|
||||
}
|
||||
|
||||
private static void dash(Player p, BlackKnifePlugin plugin) {
|
||||
if (!p.isOnline() || p.isDead()) { cleanup(p); return; }
|
||||
Vector dir = p.getEyeLocation().getDirection().normalize();
|
||||
p.setVelocity(dir.clone().multiply(DASH_SPEED));
|
||||
p.setFallDistance(0);
|
||||
immuneUntil.put(p.getUniqueId(), System.currentTimeMillis() + FALL_IMMUNE_MS);
|
||||
p.getWorld().playSound(p.getLocation(), Sound.ENTITY_ENDER_DRAGON_FLAP, 1f, 1.2f);
|
||||
|
||||
new BukkitRunnable() {
|
||||
int ticks = 0;
|
||||
boolean hit = false;
|
||||
|
||||
@Override public void run() {
|
||||
if (!p.isOnline() || p.isDead()) { cleanup(p); cancel(); return; }
|
||||
p.setFallDistance(0);
|
||||
ticks++;
|
||||
Location cur = p.getLocation().add(0, 1, 0);
|
||||
cur.getWorld().spawnParticle(Particle.DUST, cur, 2, 0.15, 0.15, 0.15, 0,
|
||||
new Particle.DustOptions(Color.fromRGB(80, 160, 255), 1.4f));
|
||||
cur.getWorld().spawnParticle(Particle.CLOUD, cur, 1, 0.1, 0.1, 0.1, 0.02);
|
||||
|
||||
for (LivingEntity le : cur.getWorld().getNearbyLivingEntities(cur, 1.2)) {
|
||||
if (le == p) continue;
|
||||
if (!DamageUtil.canHurt(p, le)) continue;
|
||||
DamageUtil.apply(p, le, DAMAGE);
|
||||
Vector kb = dir.clone().multiply(1.2);
|
||||
kb.setY(kb.getY() + 0.4);
|
||||
le.setVelocity(kb);
|
||||
le.getWorld().spawnParticle(Particle.CRIT, le.getLocation().add(0, 1, 0), 12, 0.4, 0.4, 0.4, 0.1);
|
||||
le.getWorld().playSound(le.getLocation(), Sound.ENTITY_PLAYER_ATTACK_CRIT, 1f, 0.8f);
|
||||
plugin.getTension().startBattle(p, le);
|
||||
hit = true;
|
||||
break;
|
||||
}
|
||||
if (hit) { finish(); return; }
|
||||
Location ahead = p.getEyeLocation().add(dir.clone().multiply(1.0));
|
||||
if (ahead.getBlock().isSolid()) { finish(); return; }
|
||||
if (ticks > 40) { finish(); return; }
|
||||
}
|
||||
|
||||
private void finish() {
|
||||
immuneUntil.put(p.getUniqueId(), System.currentTimeMillis() + 3000L);
|
||||
cleanup(p);
|
||||
cancel();
|
||||
}
|
||||
}.runTaskTimer(plugin, 1L, 1L);
|
||||
}
|
||||
|
||||
private static void cleanup(Player p) {
|
||||
frozen.remove(p.getUniqueId());
|
||||
diving.remove(p.getUniqueId());
|
||||
}
|
||||
|
||||
public static boolean isImmune(Player p) {
|
||||
Long until = immuneUntil.get(p.getUniqueId());
|
||||
return until != null && System.currentTimeMillis() < until;
|
||||
}
|
||||
|
||||
@EventHandler(priority = EventPriority.HIGHEST, ignoreCancelled = true)
|
||||
public void onFall(EntityDamageEvent e) {
|
||||
if (e.getCause() != EntityDamageEvent.DamageCause.FALL) return;
|
||||
if (!(e.getEntity() instanceof Player p)) return;
|
||||
if (isImmune(p) || frozen.containsKey(p.getUniqueId()) || Boolean.TRUE.equals(diving.get(p.getUniqueId()))) {
|
||||
e.setCancelled(true);
|
||||
p.setFallDistance(0);
|
||||
}
|
||||
}
|
||||
|
||||
@EventHandler(priority = EventPriority.HIGH)
|
||||
public void onMove(PlayerMoveEvent e) {
|
||||
Player p = e.getPlayer();
|
||||
Location anchor = frozen.get(p.getUniqueId());
|
||||
if (anchor == null) return;
|
||||
if (e.getTo() == null) return;
|
||||
double dx = e.getTo().getX() - anchor.getX();
|
||||
double dy = e.getTo().getY() - anchor.getY();
|
||||
double dz = e.getTo().getZ() - anchor.getZ();
|
||||
if (Math.abs(dx) > 0.05 || Math.abs(dy) > 0.05 || Math.abs(dz) > 0.05) {
|
||||
Location fixed = anchor.clone();
|
||||
fixed.setYaw(e.getTo().getYaw());
|
||||
fixed.setPitch(e.getTo().getPitch());
|
||||
e.setTo(fixed);
|
||||
p.sendActionBar(Component.text("Aiming...").color(NamedTextColor.AQUA));
|
||||
}
|
||||
}
|
||||
|
||||
@EventHandler
|
||||
public void onQuit(PlayerQuitEvent e) {
|
||||
frozen.remove(e.getPlayer().getUniqueId());
|
||||
diving.remove(e.getPlayer().getUniqueId());
|
||||
immuneUntil.remove(e.getPlayer().getUniqueId());
|
||||
}
|
||||
|
||||
@EventHandler
|
||||
public void onDeath(PlayerDeathEvent e) {
|
||||
frozen.remove(e.getEntity().getUniqueId());
|
||||
diving.remove(e.getEntity().getUniqueId());
|
||||
immuneUntil.remove(e.getEntity().getUniqueId());
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user