feat: GRENADE parabolic nade (35/5.5, 25TP, 15cd) + DIVE sky strike (25dmg knockback, 87.5TP, 120cd)
This commit is contained in:
@@ -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