Skip to content

Instantly share code, notes, and snippets.

@bristermitten
Last active April 19, 2020 14:34
Show Gist options
  • Save bristermitten/de7d6d96fd9b0f6117ce75b3a3f8ab7e to your computer and use it in GitHub Desktop.
Save bristermitten/de7d6d96fd9b0f6117ce75b3a3f8ab7e to your computer and use it in GitHub Desktop.
A simple Spigot Custom Enchant template
package me.bristermitten.enchant;
import org.bukkit.Bukkit;
import org.bukkit.NamespacedKey;
import org.bukkit.enchantments.Enchantment;
import org.bukkit.enchantments.EnchantmentTarget;
import org.bukkit.event.EventHandler;
import org.bukkit.event.Listener;
import org.bukkit.event.block.BlockBreakEvent;
import org.bukkit.inventory.ItemStack;
import org.bukkit.inventory.meta.ItemMeta;
import org.jetbrains.annotations.NotNull;
import java.lang.reflect.Field;
import java.util.ArrayList;
import java.util.List;
/**
* A simple Spigot custom enchantment template.
* This defines a simple enchantment that creates an explosion when mining a block.
* Some may prefer to have a separate Listener implementation to keep the Enchantment as a POJO.
* You can also make an intermediary abstract class to handle the boilerplate methods that don't need implementing, such as isTreasure().
*
* @author BristerMitten (github.com/knightzmc)
*/
public class BreakerEnchant extends Enchantment implements Listener {
/**
* Keep it a Singleton - it makes things easier
*/
public static final BreakerEnchant INSTANCE = new BreakerEnchant();
/**
* ID of the enchantment. Shouldn't conflict with any others.
* Note that in newer Minecraft Versions (>1.15) this uses a NamespacedKey instead of an int for the ID.
*/
private static final int ID = 500;
/**
* Our Enchantment name.
*/
private static final String NAME = "Breaker";
/**
* No other instances allowed. We should only refer to our Singleton instance
*/
private BreakerEnchant() {
//super(ID); //For < 1.15
super(new NamespacedKey(YourPluginInstance, NAME)); //For 1.15
}
@Override
@NotNull
public String getName() {
return NAME;
}
/*
* Implement as required. Must be more than the start level.
* Note that this line defines where addUnsafeEnchants must be used.
*/
@Override
public int getMaxLevel() {
return 5;
}
/*
* Implement as required.
*/
@Override
public int getStartLevel() {
return 0;
}
/*
* Implement as required
*/
@Override
@NotNull
public EnchantmentTarget getItemTarget() {
return EnchantmentTarget.ALL;
}
/*
* Implement as required
*/
@Override
public boolean isTreasure() {
return false;
}
/*
* Implement as required
*/
@Override
public boolean isCursed() {
return false;
}
/*
* Implement as required
*/
@Override
public boolean conflictsWith(@NotNull Enchantment other) {
return false;
}
/*
* Implement as required
*/
@Override
public boolean canEnchantItem(@NotNull ItemStack item) {
return true;
}
/**
* Example event handler.
* This shows the boilerplate needed for a simple handler, and can do just about anything.
* Obviously, this could handle any event, it's not specific to block breaking.
*/
@EventHandler
public void onBlockBreak(BlockBreakEvent e) {
ItemStack itemInMainHand = e.getPlayer().getInventory().getItemInMainHand();
if (!itemInMainHand.containsEnchantment(this)) return;
e.getPlayer().getWorld().createExplosion(e.getPlayer().getLocation(), 4F, false, true, e.getPlayer());
//do something cool like an explosion
}
}
/**
* In charge of registering enchantments and enchanting items.
* <p>
* Example Usage: <pre>{@code
* void onEnable() {
* EnchantManager.registerCustomEnchantments();
* //Gives level 3
* EnchantManager.enchantItem(BreakerEnchant.INSTANCE, someItem, 3);
* }
* }</pre>
*/
class EnchantManager {
/**
* You should register all of your custom enchants in here, and only once per server startup.
*
* @throws Exception if reflection fails or any enchantments are already registered
*/
public static void registerCustomEnchantments() throws Exception {
//Reflection to allow registering new enchants. It's locked by default
Field acceptingField = Enchantment.class.getDeclaredField("acceptingNew");
acceptingField.setAccessible(true);
acceptingField.set(null, true);
//Register all enchants here
Enchantment.registerEnchantment(BreakerEnchant.INSTANCE);
Bukkit.getPluginManager().registerEvents(BreakerEnchant.INSTANCE, YourPluginInstance);
}
/**
* Enchant an item.
*
* @param enchantment the enchantment to give
* @param item the item to enchant
* @param level the level to enchant the item to
*/
public static void enchantItem(@NotNull Enchantment enchantment, @NotNull ItemStack item, int level) {
ItemMeta itemMeta = item.getItemMeta();
if (itemMeta == null) return; //the item type is probably AIR
List<String> lore = itemMeta.getLore();
if (lore == null) {
lore = new ArrayList<>(1); //a nice little micro-optimization since we're only adding 1 element to the list
} else {
//remove any old lore. Note that this way of doing it could break lore ordering.
lore.removeIf(line -> line.matches(enchantment.getName() + " [0-9]+"));
}
//Note that in 1.15 getKey().getKey() should be used instead of getName()
lore.add(enchantment.getName() + " " + level);
itemMeta.setLore(lore);
item.setItemMeta(itemMeta);
//Simple as this! Glowing is handled automatically.
item.addEnchantment(enchantment, level);
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment