feat: swoon VHP lock + effects + kill obfuscation Lang ru|en config + magic 4
This commit is contained in:
@@ -27,7 +27,8 @@ public class BlackKnifeCommand implements CommandExecutor {
|
||||
}
|
||||
if (args.length >= 1 && args[0].equalsIgnoreCase("reload")) {
|
||||
if (chatConfig != null) chatConfig.load();
|
||||
sender.sendMessage(Component.text("Chat config reloaded (400 words)").color(NamedTextColor.GREEN));
|
||||
plugin.reloadConfig();
|
||||
sender.sendMessage(Component.text("Chat+config reloaded lang=" + plugin.getLang()).color(NamedTextColor.GREEN));
|
||||
return true;
|
||||
}
|
||||
sender.sendMessage(Component.text("Usage: /blackknife give [player] | /blackknife reload").color(NamedTextColor.YELLOW));
|
||||
|
||||
@@ -8,9 +8,12 @@ public class BlackKnifePlugin extends JavaPlugin {
|
||||
private MagicListener magicListener;
|
||||
private ChatConfig chatConfig;
|
||||
private ChatHandler chatHandler;
|
||||
private String lang = "ru";
|
||||
|
||||
@Override
|
||||
public void onEnable() {
|
||||
saveDefaultConfig();
|
||||
lang = getConfig().getString("lang", "ru");
|
||||
tension = new TensionManager();
|
||||
magicManager = new MagicManager(this);
|
||||
magicListener = new MagicListener(this, magicManager);
|
||||
@@ -24,15 +27,17 @@ public class BlackKnifePlugin extends JavaPlugin {
|
||||
getServer().getPluginManager().registerEvents(new ParryHandler(this), this);
|
||||
getServer().getPluginManager().registerEvents(magicListener, this);
|
||||
getServer().getPluginManager().registerEvents(chatHandler, this);
|
||||
getServer().getPluginManager().registerEvents(new KillMessageHandler(this), this);
|
||||
getCommand("blackknife").setExecutor(new BlackKnifeCommand(this, chatConfig, chatHandler));
|
||||
getCommand("magic").setExecutor(new MagicCommand(magicManager, this));
|
||||
getServer().getScheduler().runTaskTimer(this, () -> tension.tickShield(), 20L, 1L);
|
||||
getLogger().info("BlackKnife 1.0.0 enabled - Paper 1.21.1");
|
||||
getLogger().info("BlackKnife 1.0.0 enabled - Paper 1.21.1 lang=" + lang);
|
||||
}
|
||||
|
||||
public TensionManager getTension() { return tension; }
|
||||
public MagicManager getMagicManager() { return magicManager; }
|
||||
public MagicListener getMagicListener() { return magicListener; }
|
||||
public String getLang() { return lang; }
|
||||
|
||||
@Override
|
||||
public void onDisable() {
|
||||
|
||||
@@ -0,0 +1,95 @@
|
||||
package me.sashegdev.blackknife;
|
||||
|
||||
import net.kyori.adventure.text.Component;
|
||||
import net.kyori.adventure.text.format.TextDecoration;
|
||||
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.PlayerDeathEvent;
|
||||
import org.bukkit.persistence.PersistentDataType;
|
||||
import java.util.Random;
|
||||
|
||||
public class KillMessageHandler implements Listener {
|
||||
private final BlackKnifePlugin plugin;
|
||||
private final Random rnd = new Random();
|
||||
|
||||
public KillMessageHandler(BlackKnifePlugin plugin) { this.plugin = plugin; }
|
||||
|
||||
@EventHandler(priority = EventPriority.HIGHEST)
|
||||
public void onDeath(PlayerDeathEvent e) {
|
||||
Player victim = e.getEntity();
|
||||
Player killer = victim.getKiller();
|
||||
Component deathMsg = e.deathMessage();
|
||||
if (deathMsg == null) deathMsg = Component.text(victim.getName() + " died");
|
||||
boolean isRu = isRu();
|
||||
if (killer != null) {
|
||||
String killerObf = obf(killer.getName());
|
||||
Component killerComp = Component.text(killerObf).decorate(TextDecoration.OBFUSCATED);
|
||||
String magic = detectMagic(killer, victim);
|
||||
if (magic != null) {
|
||||
boolean isKnife = isKnifeMagic(magic);
|
||||
Component magicComp = isKnife ? Component.text(magic).decorate(TextDecoration.OBFUSCATED) : Component.text(magic);
|
||||
Component msg;
|
||||
if (isRu) msg = Component.text(victim.getName() + " был убит ").append(killerComp).append(Component.text(" магией ")).append(magicComp);
|
||||
else msg = Component.text(victim.getName() + " was slain by ").append(killerComp).append(Component.text("'s ")).append(magicComp);
|
||||
e.deathMessage(msg);
|
||||
return;
|
||||
}
|
||||
String plain = net.kyori.adventure.text.serializer.plain.PlainTextComponentSerializer.plainText().serialize(deathMsg);
|
||||
if (plain.contains(killer.getName())) {
|
||||
Component obf = Component.text(killerObf).decorate(TextDecoration.OBFUSCATED);
|
||||
Component replaced = replaceFirst(deathMsg, killer.getName(), obf);
|
||||
e.deathMessage(replaced);
|
||||
} else {
|
||||
Component msg = isRu ? Component.text(victim.getName() + " has swooned ").append(killerComp) : Component.text(victim.getName() + " has swooned ").append(killerComp);
|
||||
e.deathMessage(msg);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private String detectMagic(Player killer, Player victim) {
|
||||
var lastDamage = victim.getLastDamageCause();
|
||||
if (lastDamage != null && lastDamage.getCause() != null) {
|
||||
Component killerName = Component.text(killer.getName());
|
||||
}
|
||||
if (killer.getPersistentDataContainer().has(SwoonHandler.SWOONED, PersistentDataType.BYTE)) return "SWOON";
|
||||
if (BlackKnifeItem.is(killer.getInventory().getItemInMainHand()) || BlackKnifeItem.is(killer.getInventory().getItemInOffHand())) {
|
||||
String mode = killer.getPersistentDataContainer().has(BlackKnifeItem.MODE, PersistentDataType.STRING) ? killer.getPersistentDataContainer().get(BlackKnifeItem.MODE, PersistentDataType.STRING) : "Black Knife";
|
||||
return mode != null ? mode : "Black Knife";
|
||||
}
|
||||
for (MagicType mt : MagicType.values()) {
|
||||
if (killer.hasCooldown(org.bukkit.Material.AMETHYST_SHARD)) return mt.display;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
private boolean isKnifeMagic(String magic) {
|
||||
if (magic == null) return false;
|
||||
String m = magic.toLowerCase();
|
||||
return m.contains("swoon") || m.contains("sword") || m.contains("diamond") || m.contains("lunge") || m.contains("snipe") || m.contains("tri") || m.contains("overhead") || m.contains("stars") || m.contains("black knife") || m.contains("knife");
|
||||
}
|
||||
|
||||
private String obf(String name) {
|
||||
String chars = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789";
|
||||
StringBuilder sb = new StringBuilder();
|
||||
int len = 8 + rnd.nextInt(5);
|
||||
for (int i=0;i<len;i++) sb.append(chars.charAt(rnd.nextInt(chars.length())));
|
||||
return sb.toString();
|
||||
}
|
||||
|
||||
private Component replaceFirst(Component in, String target, Component repl) {
|
||||
String plain = net.kyori.adventure.text.serializer.plain.PlainTextComponentSerializer.plainText().serialize(in);
|
||||
int idx = plain.indexOf(target);
|
||||
if (idx < 0) return in;
|
||||
String before = plain.substring(0, idx);
|
||||
String after = plain.substring(idx + target.length());
|
||||
Component out = Component.text(before).append(repl).append(Component.text(after));
|
||||
return out;
|
||||
}
|
||||
|
||||
private boolean isRu() {
|
||||
String lang = plugin.getLang();
|
||||
return lang != null && lang.equalsIgnoreCase("ru");
|
||||
}
|
||||
}
|
||||
@@ -5,25 +5,37 @@ 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.entity.EntityDamageByEntityEvent;
|
||||
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<UUID, TextDisplay> displays = new HashMap<>();
|
||||
|
||||
public SwoonHandler(BlackKnifePlugin plugin) { this.plugin = plugin; }
|
||||
|
||||
@EventHandler(priority = EventPriority.HIGHEST)
|
||||
public void onHit(EntityDamageByEntityEvent e) {
|
||||
public void onHit(org.bukkit.event.entity.EntityDamageByEntityEvent e) {
|
||||
if (!(e.getDamager() instanceof Player p)) return;
|
||||
if (!BlackKnifeItem.is(p.getInventory().getItemInMainHand()) && !BlackKnifeItem.is(p.getInventory().getItemInOffHand())) return;
|
||||
if (!(e.getEntity() instanceof LivingEntity target)) return;
|
||||
@@ -35,21 +47,53 @@ public class SwoonHandler implements Listener {
|
||||
e.setCancelled(true);
|
||||
plugin.getTension().setTP(p, Math.max(0, plugin.getTension().getTP(p) - 125f));
|
||||
if (target instanceof Player tp) {
|
||||
double maxHp = tp.getAttribute(org.bukkit.attribute.Attribute.GENERIC_MAX_HEALTH).getValue();
|
||||
double vhpTmp = tp.getHealth() - 999;
|
||||
double vhp = Math.max(-2147483648.0, Math.min(maxHp, vhpTmp));
|
||||
final double finalVhp = vhp;
|
||||
tp.setHealth(1.0);
|
||||
tp.setWalkSpeed(0f);
|
||||
tp.getPersistentDataContainer().set(SWOONED, PersistentDataType.BYTE, (byte) 1);
|
||||
tp.addPotionEffect(new PotionEffect(PotionEffectType.SLOWNESS, 100, 2));
|
||||
tp.addPotionEffect(new PotionEffect(PotionEffectType.WEAKNESS, 100, 1));
|
||||
tp.addPotionEffect(new PotionEffect(PotionEffectType.DARKNESS, 60, 0));
|
||||
tp.addPotionEffect(new PotionEffect(PotionEffectType.BLINDNESS, 20, 0));
|
||||
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 td = tp.getWorld().spawn(tp.getLocation().add(0, 2.4, 0), TextDisplay.class, d -> {
|
||||
d.text(Component.text(String.format("%.0f HP", finalVhp)).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);
|
||||
displays.put(tp.getUniqueId(), td);
|
||||
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").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);
|
||||
if (Math.random() < 0.1) tp.getWorld().dropItemNaturally(tp.getLocation(), BlackKnifeItem.create());
|
||||
plugin.getServer().getScheduler().runTaskTimer(plugin, task -> {
|
||||
TextDisplay d = displays.get(tp.getUniqueId());
|
||||
if (d == null || !tp.isOnline() || !tp.getPersistentDataContainer().has(SWOONED, PersistentDataType.BYTE)) { 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()) { d.remove(); task.cancel(); }
|
||||
}, 2L, 2L);
|
||||
} 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));
|
||||
}
|
||||
@@ -59,6 +103,84 @@ public class SwoonHandler implements Listener {
|
||||
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;
|
||||
double maxHp = p.getAttribute(org.bukkit.attribute.Attribute.GENERIC_MAX_HEALTH).getValue();
|
||||
newVhp = Math.min(maxHp, newVhp);
|
||||
p.getPersistentDataContainer().set(VHP, PersistentDataType.DOUBLE, newVhp);
|
||||
TextDisplay td = displays.get(p.getUniqueId());
|
||||
if (td != null) td.text(Component.text(String.format("%.0f HP", newVhp)).color(newVhp < 0 ? NamedTextColor.DARK_RED : newVhp < 1 ? NamedTextColor.YELLOW : NamedTextColor.GREEN));
|
||||
if (newVhp >= 1) {
|
||||
p.getPersistentDataContainer().remove(SWOONED);
|
||||
p.getPersistentDataContainer().remove(VHP);
|
||||
p.setHealth(Math.min(newVhp, maxHp));
|
||||
p.setWalkSpeed(0.2f);
|
||||
p.removePotionEffect(PotionEffectType.SLOWNESS);
|
||||
p.removePotionEffect(PotionEffectType.JUMP_BOOST);
|
||||
p.removePotionEffect(PotionEffectType.WEAKNESS);
|
||||
p.removePotionEffect(PotionEffectType.DARKNESS);
|
||||
if (td != null) { td.remove(); displays.remove(p.getUniqueId()); }
|
||||
p.sendActionBar(Component.text("Revived from SWOON!").color(NamedTextColor.GREEN));
|
||||
p.getWorld().playSound(p.getLocation(), Sound.BLOCK_BEACON_ACTIVATE, 1f, 1.2f);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@EventHandler
|
||||
public void onDeath(PlayerDeathEvent e) {
|
||||
displays.remove(e.getEntity().getUniqueId());
|
||||
e.getEntity().getPersistentDataContainer().remove(SWOONED);
|
||||
}
|
||||
|
||||
public static boolean isSwooned(LivingEntity e) {
|
||||
return e.getPersistentDataContainer().has(SWOONED, PersistentDataType.BYTE);
|
||||
}
|
||||
|
||||
@@ -0,0 +1 @@
|
||||
lang: ru
|
||||
Reference in New Issue
Block a user