package fr.she3py.ce;
import java.lang.reflect.Field;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Map;
import java.util.function.Consumer;
import java.util.logging.Level;
import java.util.stream.Collectors;
import org.bukkit.Bukkit;
import org.bukkit.Location;
import org.bukkit.Material;
import org.bukkit.NamespacedKey;
import org.bukkit.attribute.Attribute;
import org.bukkit.command.Command;
import org.bukkit.command.CommandExecutor;
import org.bukkit.command.CommandSender;
import org.bukkit.command.TabCompleter;
import org.bukkit.craftbukkit.v1_13_R2.CraftWorld;
import org.bukkit.craftbukkit.v1_13_R2.entity.CraftEntity;
import org.bukkit.enchantments.Enchantment;
import org.bukkit.enchantments.EnchantmentTarget;
import org.bukkit.entity.Arrow;
import org.bukkit.entity.EntityType;
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.EntityDamageByEntityEvent;
import org.bukkit.event.entity.EntityDamageEvent.DamageModifier;
import org.bukkit.event.entity.ProjectileHitEvent;
import org.bukkit.event.entity.ProjectileLaunchEvent;
import org.bukkit.event.player.PlayerTeleportEvent.TeleportCause;
import org.bukkit.inventory.ItemStack;
import org.bukkit.inventory.meta.ItemMeta;
import org.bukkit.metadata.FixedMetadataValue;
import org.bukkit.plugin.java.JavaPlugin;
import org.bukkit.potion.PotionEffect;
import org.bukkit.potion.PotionEffectType;
@SuppressWarnings("deprecation")
public class CustomEnchants extends JavaPlugin implements Listener {
@SuppressWarnings("unchecked")
@Override
public void onEnable() {
try {
final Field f0 = Enchantment.class.getDeclaredField("byKey");
f0.setAccessible(true);
final Map<NamespacedKey, Enchantment> byKey = (Map<NamespacedKey, Enchantment>) f0.get(null);
final Field f1 = Enchantment.class.getDeclaredField("byName");
f1.setAccessible(true);
final Map<String, Enchantment> byName = (Map<String, Enchantment>) f1.get(null);
Arrays.asList(Enchants.values()).forEach(e -> {
byKey.put(e.ench.getKey(), e.ench);
byName.put(e.ench.getName(), e.ench);
});
Bukkit.getPluginManager().registerEvents(this, this);
final EnchCommand cmd = new EnchCommand();
this.getCommand("ench").setExecutor(cmd);
this.getCommand("ench").setTabCompleter(cmd);
} catch(Exception e) {
this.getLogger().log(Level.SEVERE, "Unable to enable the plugin", e);
Bukkit.getPluginManager().disablePlugin(this);
}
}
@EventHandler(priority = EventPriority.HIGH)
public void onEntityDamagedByEntity(final EntityDamageByEntityEvent e) {
if(!(e.getEntity() instanceof LivingEntity)) return;
if(e.getDamager() instanceof Player) {
final ItemStack item = ((Player) e.getDamager()).getInventory().getItemInMainHand();
if(item == null || item.getType() == Material.AIR || !item.hasItemMeta() || !item.getItemMeta().hasEnchants()) return;
item.getItemMeta().getEnchants().keySet().forEach(e0 -> { if(Enchants.has(e0)) Enchants.get(e0).handler.accept(e); });
}
else if(e.getDamager() instanceof Arrow) {
final Arrow arrow = (Arrow) e.getDamager();
if(arrow.hasMetadata("wither"))
Enchants.WITHERBOW.handler.accept(e);
if(arrow.hasMetadata("perforation"))
Enchants.PERFORATION.handler.accept(e);
}
}
@EventHandler(priority = EventPriority.LOW)
public void onProjectileLaunch(final ProjectileLaunchEvent e) {
if(!(e.getEntityType() == EntityType.ARROW && e.getEntity().getShooter() instanceof Player)) return;
final Arrow arrow = (Arrow) e.getEntity();
final ItemStack item = ((Player) arrow.getShooter()).getInventory().getItemInMainHand();
if(item == null || item.getType() == Material.AIR || !item.hasItemMeta()) return;
final ItemMeta meta = item.getItemMeta();
if(meta.hasEnchant(Enchants.PERFORATION.ench))
arrow.setMetadata("perforation", new FixedMetadataValue(this, null));
if(meta.hasEnchant(Enchants.WITHERBOW.ench))
arrow.setMetadata("wither", new FixedMetadataValue(this, null));
if(meta.hasEnchant(Enchants.EXPLOSIVE.ench))
arrow.setMetadata("explosive", new FixedMetadataValue(this, null));
if(meta.hasEnchant(Enchants.LIGHTNING.ench))
arrow.setMetadata("lightning", new FixedMetadataValue(this, null));
if(meta.hasEnchant(Enchants.TELEPORTATION.ench))
arrow.setMetadata("teleport", new FixedMetadataValue(this, null));
}
@EventHandler(priority = EventPriority.HIGH)
public void onProjectileHit(final ProjectileHitEvent e) {
if(e.getEntityType() != EntityType.ARROW) return;
final Arrow arrow = (Arrow) e.getEntity();
final Location l = arrow.getLocation();
if(arrow.hasMetadata("lightning"))
arrow.getWorld().strikeLightning(l);
if(arrow.hasMetadata("explosive"))
((CraftWorld) arrow.getWorld()).getHandle().createExplosion(((CraftEntity) arrow.getShooter()).getHandle(), l.getX(), l.getY(), l.getZ(), 4F, false, false);
if(arrow.hasMetadata("teleport"))
((LivingEntity) arrow.getShooter()).teleport(l, TeleportCause.PLUGIN);
// avoid redoing the effects if the arrow falls
arrow.removeMetadata("explosive", this);
arrow.removeMetadata("lightning", this);
arrow.removeMetadata("teleport", this);
}
private static enum Enchants {
WITHER("Wither", EnchantmentTarget.WEAPON, e -> ((LivingEntity) e.getEntity()).addPotionEffect(new PotionEffect(PotionEffectType.WITHER, 100, 1))),
POISON("Poison", EnchantmentTarget.WEAPON, e -> ((LivingEntity) e.getEntity()).addPotionEffect(new PotionEffect(PotionEffectType.POISON, 100, 1))),
SLOW("Slow", EnchantmentTarget.WEAPON, e -> ((LivingEntity) e.getEntity()).addPotionEffect(new PotionEffect(PotionEffectType.SLOW, 100, 1))),
BLINDNESS("Blindness", EnchantmentTarget.WEAPON, e -> ((LivingEntity) e.getEntity()).addPotionEffect(new PotionEffect(PotionEffectType.BLINDNESS, 100, 1))),
LIFESTEAL("Lifesteal", EnchantmentTarget.WEAPON, e -> {
final Player dmger = (Player) e.getDamager();
dmger.setHealth(Math.min(dmger.getHealth() + e.getDamage() * 0.2, dmger.getAttribute(Attribute.GENERIC_MAX_HEALTH).getValue()));
}),
WITHERBOW("Wither", EnchantmentTarget.BOW, e -> ((LivingEntity) e.getEntity()).addPotionEffect(new PotionEffect(PotionEffectType.WITHER, 100, 1))),
PERFORATION("Perforation", EnchantmentTarget.BOW, e -> e.setDamage(DamageModifier.ARMOR, -0)),
TELEPORTATION("Teleportation", EnchantmentTarget.BOW, e -> {}),
EXPLOSIVE("Explosive", EnchantmentTarget.BOW, e -> {}),
LIGHTNING("Lightning", EnchantmentTarget.BOW, e -> {});
private final Enchantment ench;
private final Consumer<EntityDamageByEntityEvent> handler;
private Enchants(final String name, final EnchantmentTarget target, final Consumer<EntityDamageByEntityEvent> handler) {
this.ench = new Enchantment(new NamespacedKey("customenchants", name().toLowerCase())) {
@Override
public boolean isTreasure() {
return false;
}
@Override
public boolean isCursed() {
return false;
}
@Override
public int getStartLevel() {
return 1;
}
@Override
public String getName() {
return name;
}
@Override
public int getMaxLevel() {
return 1;
}
@Override
public EnchantmentTarget getItemTarget() {
return target;
}
@Override
public boolean conflictsWith(final Enchantment other) {
return false;
}
@Override
public boolean canEnchantItem(final ItemStack item) {
return target.includes(item);
}
};
this.handler = handler;
}
public static Enchants get(final Enchantment e) {
for(int i = 0; i < Enchants.values().length; i++)
if(Enchants.values()[i].ench == e) return Enchants.values()[i];
return null;
}
public static Enchants get(final String name) {
for(int i = 0; i < Enchants.values().length; i++)
if(Enchants.values()[i].ench.getName().equals(name)) return Enchants.values()[i];
return null;
}
public static boolean has(final Enchantment e) {
return get(e) != null;
}
public void addTo(final ItemStack item) {
final ItemMeta meta = item.getItemMeta();
if(meta.hasEnchant(ench)) return;
meta.addEnchant(ench, 1, false);
final List<String> lore = (meta.hasLore()) ? meta.getLore() : new ArrayList<>();
lore.add(0, "§r§7" + ench.getName());
meta.setLore(lore);
item.setItemMeta(meta);
}
}
private static class EnchCommand implements CommandExecutor, TabCompleter {
@Override
public boolean onCommand(final CommandSender sender, final Command command, final String label, final String[] args) {
if(!(sender instanceof LivingEntity)) { sender.sendMessage("§cYou must be a living entity to do that."); return true; }
if(!sender.hasPermission("customenchants.use")) { sender.sendMessage("§cYou are not allowed to do this."); return true; }
if(args.length != 1) return false;
final ItemStack item = ((LivingEntity) sender).getEquipment().getItemInMainHand();
if(item == null || item.getType() == Material.AIR) { sender.sendMessage("§cYou must have an item in your hand to do this."); return true; }
final Enchants ench = Enchants.get(args[0]);
if(ench == null) { sender.sendMessage("§cUnknown enchantment."); return true; }
if(!ench.ench.canEnchantItem(item)) { sender.sendMessage("§cThis enchantment can not be applied on this item."); return true; }
ench.addTo(item);
sender.sendMessage("The specified enchantment has been added to your item.");
return true;
}
@Override
public List<String> onTabComplete(final CommandSender sender, final Command command, final String alias, final String[] args) {
if(!(sender instanceof LivingEntity) || !sender.hasPermission("customenchants.use")) return new ArrayList<>();
if(args.length != 1) return new ArrayList<>();
final ItemStack item = ((LivingEntity) sender).getEquipment().getItemInMainHand();
if(item == null || item.getType() == Material.AIR) return new ArrayList<>();
return Arrays.asList(Enchants.values())
.stream()
.filter(ench -> ench.ench.canEnchantItem(item) && !item.getItemMeta().hasEnchant(ench.ench))
.map(ench -> ench.ench.getName())
.collect(Collectors.toList());
}
}
}