package me.sashegdev.blackknife; import net.kyori.adventure.text.Component; import net.kyori.adventure.text.format.NamedTextColor; import org.bukkit.Material; import org.bukkit.NamespacedKey; import org.bukkit.Sound; import org.bukkit.entity.Display; import org.bukkit.entity.LivingEntity; import org.bukkit.entity.Player; import org.bukkit.entity.TextDisplay; import org.bukkit.event.EventHandler; import org.bukkit.event.EventPriority; import org.bukkit.event.Listener; import org.bukkit.event.block.BlockBreakEvent; import org.bukkit.event.block.BlockPlaceEvent; import org.bukkit.event.entity.EntityDamageEvent; import org.bukkit.event.entity.PlayerDeathEvent; import org.bukkit.event.player.PlayerInteractEvent; import org.bukkit.event.player.PlayerMoveEvent; import org.bukkit.persistence.PersistentDataType; import org.bukkit.potion.PotionEffect; import org.bukkit.potion.PotionEffectType; import java.util.HashMap; import java.util.Map; import java.util.UUID; public class SwoonHandler implements Listener { private final BlackKnifePlugin plugin; public static final NamespacedKey SWOONED = new NamespacedKey("blackknife", "swooned"); public static final NamespacedKey MAGIC_DAMAGE = new NamespacedKey("blackknife", "magic_damage"); public static final NamespacedKey VHP = new NamespacedKey("blackknife", "vhp"); private final Map displays = new HashMap<>(); public SwoonHandler(BlackKnifePlugin plugin) { this.plugin = plugin; } @EventHandler(priority = EventPriority.HIGHEST, ignoreCancelled = true) public void onHit(org.bukkit.event.entity.EntityDamageByEntityEvent e) { if (!(e.getDamager() instanceof Player p)) return; if (p.getGameMode() == org.bukkit.GameMode.SPECTATOR) return; if (!BlackKnifeItem.is(p.getInventory().getItemInMainHand()) && !BlackKnifeItem.is(p.getInventory().getItemInOffHand())) return; if (!(e.getEntity() instanceof LivingEntity target)) return; if (target.getPersistentDataContainer().has(MAGIC_DAMAGE, PersistentDataType.BYTE)) { target.getPersistentDataContainer().remove(MAGIC_DAMAGE); return; } if (target.getPersistentDataContainer().has(SWOONED, PersistentDataType.BYTE)) return; if (target instanceof Player vt) { if (vt.getGameMode() == org.bukkit.GameMode.CREATIVE || vt.getGameMode() == org.bukkit.GameMode.SPECTATOR) return; if (vt.isInvulnerable()) return; } if (p.hasCooldown(Material.NETHERITE_SWORD)) return; e.setCancelled(true); plugin.getTension().setTP(p, Math.max(0, plugin.getTension().getTP(p) - 125f)); if (target instanceof Player tp) { var attr = tp.getAttribute(org.bukkit.attribute.Attribute.GENERIC_MAX_HEALTH); double maxHp = attr != null ? attr.getValue() : 20.0; double vhpTmp = tp.getHealth() - 999; double vhp = Math.max(-2147483648.0, Math.min(maxHp, vhpTmp)); final double finalVhp = vhp; applySwoon(tp, vhp); tp.getWorld().spawnParticle(org.bukkit.Particle.SOUL, tp.getLocation().add(0,1,0), 40, 0.5,0.5,0.5,0.05); tp.getWorld().spawnParticle(org.bukkit.Particle.SCULK_SOUL, tp.getLocation().add(0,1,0), 15, 0.5,0.5,0.5,0.02); tp.getWorld().spawnParticle(org.bukkit.Particle.ASH, tp.getLocation().add(0,1,0), 20, 0.5,0.5,0.5,0); for (int k=0;k<12;k++){ double a=(double)k/12*Math.PI*2; org.bukkit.Location r=tp.getLocation().add(Math.cos(a)*1.5,0.1,Math.sin(a)*1.5); r.getWorld().spawnParticle(org.bukkit.Particle.SMOKE,r,2,0.05,0.05,0.05,0); r.getWorld().spawnParticle(org.bukkit.Particle.SCULK_SOUL,r,1,0.02,0.02,0.02,0); } p.sendActionBar(Component.text("SWOON! " + tp.getName() + " -999 HP").color(NamedTextColor.DARK_RED)); tp.sendActionBar(Component.text("SWOONED! -999 HP regen to revive").color(NamedTextColor.DARK_RED)); tp.getWorld().playSound(tp.getLocation(), Sound.ENTITY_WARDEN_SONIC_BOOM, 1f, 0.5f); tp.getWorld().playSound(tp.getLocation(), Sound.ENTITY_ELDER_GUARDIAN_CURSE, 1f, 0.7f); startDisplayTask(tp); } else { target.getPersistentDataContainer().set(MAGIC_DAMAGE, PersistentDataType.BYTE, (byte) 1); target.damage(999.0, p); target.getPersistentDataContainer().remove(MAGIC_DAMAGE); target.getWorld().spawnParticle(org.bukkit.Particle.EXPLOSION, target.getLocation().add(0, 1, 0), 1); target.getWorld().spawnParticle(org.bukkit.Particle.SOUL, target.getLocation().add(0,1,0), 30, 0.5,0.5,0.5,0.05); target.getWorld().playSound(target.getLocation(), Sound.ENTITY_WARDEN_SONIC_BOOM, 1f, 0.5f); p.sendActionBar(Component.text("SWOON! " + target.getName() + " -999").color(NamedTextColor.DARK_RED)); } target.getWorld().spawnParticle(org.bukkit.Particle.SWEEP_ATTACK, target.getLocation().add(0, 1, 0), 1); target.getWorld().spawnParticle(org.bukkit.Particle.SOUL, target.getLocation().add(0, 1, 0), 20, 0.5, 0.5, 0.5, 0.02); p.setCooldown(Material.NETHERITE_SWORD, 160); p.playSound(p.getLocation(), Sound.ITEM_TRIDENT_THUNDER, 1f, 0.7f); } @EventHandler(priority = EventPriority.HIGHEST) public void onDamage(EntityDamageEvent e) { if (!(e.getEntity() instanceof Player p)) return; if (!p.getPersistentDataContainer().has(SWOONED, PersistentDataType.BYTE)) return; e.setCancelled(true); } @EventHandler public void onMove(PlayerMoveEvent e) { Player p = e.getPlayer(); if (!p.getPersistentDataContainer().has(SWOONED, PersistentDataType.BYTE)) return; if (e.getFrom().distanceSquared(e.getTo()) < 0.0001) return; double dx = e.getTo().getX() - e.getFrom().getX(); double dz = e.getTo().getZ() - e.getFrom().getZ(); if (Math.abs(dx) > 0.01 || Math.abs(dz) > 0.01 || Math.abs(e.getTo().getY() - e.getFrom().getY()) > 0.01) { e.setCancelled(true); p.sendActionBar(Component.text("SWOONED - can't move, regen to revive").color(NamedTextColor.DARK_RED)); } } @EventHandler public void onInteract(PlayerInteractEvent e) { Player p = e.getPlayer(); if (!p.getPersistentDataContainer().has(SWOONED, PersistentDataType.BYTE)) return; e.setCancelled(true); p.sendActionBar(Component.text("SWOONED - can't interact").color(NamedTextColor.DARK_RED)); } @EventHandler public void onBreak(BlockBreakEvent e) { if (!e.getPlayer().getPersistentDataContainer().has(SWOONED, PersistentDataType.BYTE)) return; e.setCancelled(true); } @EventHandler public void onPlace(BlockPlaceEvent e) { if (!e.getPlayer().getPersistentDataContainer().has(SWOONED, PersistentDataType.BYTE)) return; e.setCancelled(true); } @EventHandler public void onPotionSplash(org.bukkit.event.entity.PotionSplashEvent e) { if (e.getPotion().getEffects().stream().noneMatch(ef -> ef.getType().equals(PotionEffectType.REGENERATION))) return; for (LivingEntity ent : e.getAffectedEntities()) { if (!(ent instanceof Player p)) continue; if (!p.getPersistentDataContainer().has(SWOONED, PersistentDataType.BYTE)) continue; double intensity = e.getIntensity(p); double heal = 4 * intensity; Double vhp = p.getPersistentDataContainer().get(VHP, PersistentDataType.DOUBLE); if (vhp == null) vhp = -10.0; double newVhp = vhp + heal; var attr = p.getAttribute(org.bukkit.attribute.Attribute.GENERIC_MAX_HEALTH); double maxHp = attr != null ? attr.getValue() : 20.0; newVhp = Math.min(maxHp, newVhp); p.getPersistentDataContainer().set(VHP, PersistentDataType.DOUBLE, newVhp); TextDisplay td = displays.get(p.getUniqueId()); if (td != null && td.isValid()) td.text(Component.text(String.format("%.0f HP", newVhp)).color(newVhp < 0 ? NamedTextColor.DARK_RED : newVhp < 1 ? NamedTextColor.YELLOW : NamedTextColor.GREEN)); if (newVhp >= 1) { revive(p); } } } private void applySwoon(Player tp, double vhp) { tp.setHealth(1.0); try { tp.setWalkSpeed(0f); } catch (Exception ignored) {} tp.getPersistentDataContainer().set(SWOONED, PersistentDataType.BYTE, (byte) 1); tp.getPersistentDataContainer().set(VHP, PersistentDataType.DOUBLE, vhp); tp.addPotionEffect(new PotionEffect(PotionEffectType.SLOWNESS, 100000, 10, false, false, true)); tp.addPotionEffect(new PotionEffect(PotionEffectType.JUMP_BOOST, 100000, 128, false, false, true)); tp.addPotionEffect(new PotionEffect(PotionEffectType.WEAKNESS, 100000, 10, false, false, true)); tp.addPotionEffect(new PotionEffect(PotionEffectType.DARKNESS, 100000, 0, false, false, true)); tp.addPotionEffect(new PotionEffect(PotionEffectType.BLINDNESS, 100, 0, false, false, true)); TextDisplay old = displays.remove(tp.getUniqueId()); if (old != null && old.isValid()) { EntityTracker.untrack(old); old.remove(); } TextDisplay td = tp.getWorld().spawn(tp.getLocation().add(0, 2.4, 0), TextDisplay.class, d -> { d.text(Component.text(String.format("%.0f HP", vhp)).color(NamedTextColor.DARK_RED)); d.setBillboard(Display.Billboard.CENTER); d.setSeeThrough(true); d.setShadowed(true); d.setBackgroundColor(org.bukkit.Color.fromARGB(64, 40, 0, 0)); d.setAlignment(TextDisplay.TextAlignment.CENTER); d.getPersistentDataContainer().set(SWOONED, PersistentDataType.BYTE, (byte) 1); }); EntityTracker.mark(td); EntityTracker.smooth(td); displays.put(tp.getUniqueId(), td); } private void startDisplayTask(Player tp) { plugin.getServer().getScheduler().runTaskTimer(plugin, task -> { TextDisplay d = displays.get(tp.getUniqueId()); if (d == null || !d.isValid() || !tp.isOnline() || !tp.getPersistentDataContainer().has(SWOONED, PersistentDataType.BYTE)) { if (d != null && !d.isValid()) displays.remove(tp.getUniqueId()); task.cancel(); return; } d.teleport(tp.getLocation().add(0, 2.4, 0)); Double cur = tp.getPersistentDataContainer().get(VHP, PersistentDataType.DOUBLE); if (cur != null) d.text(Component.text(String.format("%.0f HP", cur)).color(cur < 0 ? NamedTextColor.DARK_RED : NamedTextColor.YELLOW)); if (tp.isDead()) { EntityTracker.untrack(d); d.remove(); displays.remove(tp.getUniqueId()); task.cancel(); } }, 2L, 2L); } public void revive(Player p) { Double stored = p.getPersistentDataContainer().get(VHP, PersistentDataType.DOUBLE); p.getPersistentDataContainer().remove(SWOONED); p.getPersistentDataContainer().remove(VHP); var attr = p.getAttribute(org.bukkit.attribute.Attribute.GENERIC_MAX_HEALTH); double maxHp = attr != null ? attr.getValue() : 20.0; double hp = stored != null ? Math.min(Math.max(stored, 1.0), maxHp) : 1.0; try { p.setHealth(hp); } catch (Exception ignored) {} try { p.setWalkSpeed(0.2f); } catch (Exception ignored) {} p.removePotionEffect(PotionEffectType.SLOWNESS); p.removePotionEffect(PotionEffectType.JUMP_BOOST); p.removePotionEffect(PotionEffectType.WEAKNESS); p.removePotionEffect(PotionEffectType.DARKNESS); p.removePotionEffect(PotionEffectType.BLINDNESS); TextDisplay td = displays.remove(p.getUniqueId()); if (td != null && td.isValid()) { EntityTracker.untrack(td); td.remove(); } p.sendActionBar(Component.text("Revived from SWOON!").color(NamedTextColor.GREEN)); p.getWorld().playSound(p.getLocation(), Sound.BLOCK_BEACON_ACTIVATE, 1f, 1.2f); } public void cleanupOffline(Player p) { TextDisplay td = displays.remove(p.getUniqueId()); if (td != null && td.isValid()) { EntityTracker.untrack(td); td.remove(); } } @EventHandler public void onQuit(org.bukkit.event.player.PlayerQuitEvent e) { cleanupOffline(e.getPlayer()); } @EventHandler public void onJoin(org.bukkit.event.player.PlayerJoinEvent e) { Player p = e.getPlayer(); if (!p.getPersistentDataContainer().has(SWOONED, PersistentDataType.BYTE)) return; Double vhp = p.getPersistentDataContainer().get(VHP, PersistentDataType.DOUBLE); if (vhp == null) { p.getPersistentDataContainer().remove(SWOONED); try { p.setWalkSpeed(0.2f); } catch (Exception ignored) {} return; } applySwoon(p, vhp); startDisplayTask(p); } @EventHandler public void onRespawn(org.bukkit.event.player.PlayerRespawnEvent e) { Player p = e.getPlayer(); if (!p.getPersistentDataContainer().has(SWOONED, PersistentDataType.BYTE)) return; p.getPersistentDataContainer().remove(SWOONED); p.getPersistentDataContainer().remove(VHP); try { p.setWalkSpeed(0.2f); } catch (Exception ignored) {} p.removePotionEffect(PotionEffectType.SLOWNESS); p.removePotionEffect(PotionEffectType.JUMP_BOOST); p.removePotionEffect(PotionEffectType.WEAKNESS); p.removePotionEffect(PotionEffectType.DARKNESS); p.removePotionEffect(PotionEffectType.BLINDNESS); cleanupOffline(p); } @EventHandler public void onDeath(PlayerDeathEvent e) { Player p = e.getEntity(); TextDisplay td = displays.remove(p.getUniqueId()); if (td != null && td.isValid()) { EntityTracker.untrack(td); td.remove(); } p.getPersistentDataContainer().remove(SWOONED); p.getPersistentDataContainer().remove(VHP); } public static boolean isSwooned(LivingEntity e) { return e.getPersistentDataContainer().has(SWOONED, PersistentDataType.BYTE); } }