From 0073736c0daa51166a720add2a6b9d7449619d4a Mon Sep 17 00:00:00 2001 From: root Date: Thu, 24 Sep 2026 19:07:50 +0000 Subject: [PATCH] fix DIVE: allow creative, feedback messages, segment hit sampling; tune GRENADE faster flight + 10t cd --- .../blackknife/BlackKnifeListener.java | 2 +- .../sashegdev/blackknife/GrenadeAttack.java | 6 ++- .../sashegdev/blackknife/SkyDiveAttack.java | 46 +++++++++++++------ 3 files changed, 37 insertions(+), 17 deletions(-) diff --git a/src/main/java/me/sashegdev/blackknife/BlackKnifeListener.java b/src/main/java/me/sashegdev/blackknife/BlackKnifeListener.java index 1ba3b9d..9f71c6c 100644 --- a/src/main/java/me/sashegdev/blackknife/BlackKnifeListener.java +++ b/src/main/java/me/sashegdev/blackknife/BlackKnifeListener.java @@ -169,7 +169,7 @@ public class BlackKnifeListener implements Listener { } } else if (m.equals("GRENADE")) { if (GrenadeAttack.tryUse(p, tm)) { - p.setCooldown(item.getType(), 15); + p.setCooldown(item.getType(), 10); } } else if (m.equals("DIVE")) { if (SkyDiveAttack.tryUse(p, tm)) { diff --git a/src/main/java/me/sashegdev/blackknife/GrenadeAttack.java b/src/main/java/me/sashegdev/blackknife/GrenadeAttack.java index ef0d125..67e3faa 100644 --- a/src/main/java/me/sashegdev/blackknife/GrenadeAttack.java +++ b/src/main/java/me/sashegdev/blackknife/GrenadeAttack.java @@ -24,6 +24,8 @@ public class GrenadeAttack { private static final double LETHAL_DAMAGE = 35.0; private static final double CHIP_DAMAGE = 5.5; private static final double MAX_RANGE = 15.0; + private static final double TICKS_PER_BLOCK = 2.0; + private static final int MIN_DURATION = 8; 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; @@ -39,7 +41,7 @@ public class GrenadeAttack { Vector dir = eye.getDirection().normalize(); Location target = computeTarget(p, eye, dir); double dist = eye.distance(target); - int duration = Math.max(10, (int) (dist * 3)); + int duration = Math.max(MIN_DURATION, (int) (dist * TICKS_PER_BLOCK)); 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(); @@ -133,7 +135,7 @@ public class GrenadeAttack { flightStart = cur.clone(); flightTarget = nt.clone(); double remaining = Math.max(1.0, cur.distance(nt)); - flightDuration = Math.max(6, (int) (remaining * 3)); + flightDuration = Math.max(6, (int) (remaining * TICKS_PER_BLOCK)); elapsed = 0; arc = Math.max(ARC_MIN, Math.min(ARC_MAX, remaining * ARC_PER_BLOCK)); targetWasSolid = flightTarget.getBlock().isSolid(); diff --git a/src/main/java/me/sashegdev/blackknife/SkyDiveAttack.java b/src/main/java/me/sashegdev/blackknife/SkyDiveAttack.java index 2782996..3b3dd3e 100644 --- a/src/main/java/me/sashegdev/blackknife/SkyDiveAttack.java +++ b/src/main/java/me/sashegdev/blackknife/SkyDiveAttack.java @@ -37,8 +37,14 @@ public class SkyDiveAttack implements Listener { private static final Map 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 (SwoonHandler.isSwooned(p)) { + p.sendActionBar(Component.text("SWOONED - can't use DIVE").color(NamedTextColor.RED)); + return false; + } + if (p.getGameMode() == GameMode.SPECTATOR) { + p.sendActionBar(Component.text("Spectators can't use DIVE").color(NamedTextColor.RED)); + return false; + } if (Boolean.TRUE.equals(diving.get(p.getUniqueId()))) { p.sendActionBar(Component.text("Already diving!").color(NamedTextColor.RED)); return false; @@ -60,6 +66,7 @@ public class SkyDiveAttack implements Listener { 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); + p.sendActionBar(Component.text("SkyDive!").color(NamedTextColor.AQUA)); new BukkitRunnable() { final double startY = p.getLocation().getY(); @@ -88,6 +95,7 @@ public class SkyDiveAttack implements Listener { p.setFallDistance(0); frozen.put(p.getUniqueId(), top); p.getWorld().playSound(top, Sound.BLOCK_BEACON_ACTIVATE, 0.8f, 1.4f); + p.sendActionBar(Component.text("Aim with your camera!").color(NamedTextColor.AQUA)); new BukkitRunnable() { long left = FREEZE_TICKS; @@ -125,10 +133,12 @@ public class SkyDiveAttack implements Listener { 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); + p.sendActionBar(Component.text("DIVE!").color(NamedTextColor.DARK_RED)); new BukkitRunnable() { int ticks = 0; boolean hit = false; + Location prev = p.getLocation().add(0, 1, 0).clone(); @Override public void run() { if (!p.isOnline() || p.isDead()) { cleanup(p); cancel(); return; } @@ -139,19 +149,27 @@ public class SkyDiveAttack implements Listener { 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; + // Семплируем весь пройденный за тик отрезок, иначе на скорости 2.8 цель проскакивает между тиками + Vector seg = cur.clone().subtract(prev).toVector(); + double len = seg.length(); + int steps = Math.max(1, (int) Math.ceil(len / 0.5)); + for (int s = 0; s <= steps && !hit; s++) { + Location check = prev.clone().add(seg.clone().multiply((double) s / steps)); + for (LivingEntity le : check.getWorld().getNearbyLivingEntities(check, 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; + } } + prev = cur.clone(); if (hit) { finish(); return; } Location ahead = p.getEyeLocation().add(dir.clone().multiply(1.0)); if (ahead.getBlock().isSolid()) { finish(); return; }