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

This commit is contained in:
SashegDev
2026-09-24 19:07:50 +00:00
parent 014a823c1b
commit 3e26bb9ad5
3 changed files with 37 additions and 17 deletions
@@ -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; }