fix DIVE: allow creative, feedback messages, segment hit sampling; tune GRENADE faster flight + 10t cd

This commit is contained in:
root
2026-09-24 19:07:50 +00:00
parent b0c6d9128d
commit 0073736c0d
3 changed files with 37 additions and 17 deletions
@@ -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)) {
@@ -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();
@@ -37,8 +37,14 @@ public class SkyDiveAttack implements Listener {
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 (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; }