Skip to content

Custom Enchants

Redempt edited this page Apr 9, 2021 · 4 revisions

Plugins will often find themselves wanting to add more functionality to tools. In most cases, this functionality is custom enchantments (though sometimes it's not). Whether it's explicitly enchantments or not, the Custom Enchantments library makes it easier than ever to create custom functionality for tools and armor.

To start, you need to make a class which extends CustomEnchant. Here's an example of how you can write a custom enchantment that would just say "hi" to a player when they break a block with an item enchanted with it.

public class TestEnchant extends CustomEnchant {

	public TestEnchant() {
		super("Test", 1);
		addTrigger(EnchantTrigger.MINE_BLOCK, (event, level) -> {
			event.getPlayer().sendMessage("You mined a block!");
		});
	}

}

The most important thing to note is the call to the super constructor here. It gives 2 important pieces of information: The name of the enchantment and the max level. After this, we call addTrigger, which adds a trigger that will activate the enchantment, and a lambda to be called when the event is fired.

The EnchantTrigger is what will call the lambda when a certain thing happens. The MINE_BLOCK trigger will call activate when a player mines a block holding a pickaxe that has your custom enchant on it.

Optionally, a second listener lambda can be passed for when the enchant is deactivated. This is only called for certain triggers, such as EnchantTrigger.EQUIP_ARMOR or EnchantTrigger.HOLD_ITEM. For these triggers, the first lambda, activate, is called when the enchanted armor is equipped or item held, respectively. The second lambda, deactivate, is called when the enchanted armor is unequipped or item no longer held, respectively.

Once you've written the class defining the metadata and behavior of your custom enchantment, you can easily register and load it. You do this through the EnchantRegistry class. In your onEnable, you can do this:

@Override
public void onEnable() {
	new EnchantRegistry(this).registerAll(this);
}

This will create a new EnchantRegistry owned by your plugin, then search through the plugin's classes, loading all of your custom enchantments into itself.

When creating an EnchantRegistry, you can pass it a Function<CustomEnchant<?>, String> called the namer. The namer will be used to generate display names for enchants when they are applied to items. You can also simply pass a String, which will use it as a prefix to be prepended to enchantment names to create the display name. By default, it will simply prepend the light gray color to match vanilla enchantments.

After you've created an EnchantRegistry associated with your plugin, you can get it at any time with EnchantRegistry.get(Plugin). You can use the EnchantRegistry to fetch enchantments you've created, and then apply them to items or test for their presence. Every enchantment has both a name and an ID, where the ID is just the lowercase name with underscores instead of spaces. The ID is auto-generated. For example, if your enchant's name is Test Name, the ID will be test_name. You can use EnchantRegistry.getByName(String) to fetch a custom enchant from the registry by its name or ID. With that, you can apply it to an item:

ItemStack item; //Assume this is a valid item
CustomEnchant ench = EnchantRegistry.get(this).getByName("test");
item = ench.apply(item, 1);

And just like that, we've applied the enchantment to your item at level 1.

If you want to check the level of a custom enchant on an item, you can simply call ench.getLevel(item).

There is a special naming rule for custom enchants that have a max level of 1. Similarly to mending and infinity, enchantments which only have one level will not show a roman numeral next to their name.

There is also a special method to get all of the custom enchantments applied to an item. Calling this method will be faster than iterating each custom enchantment and calling getLevel. To get all of the custom enchantments on an item, in the form of a Map<CustomEnchant<?>, Integer>, you can call EnchantRegistry#getEnchants. This is useful for another method which allows you to combine enchantment maps similarly to how anvils combine enchantments in vanilla: EnchantRegistry#combine.

In case you want an enchantment which triggers on an event that doesn't have a trigger built into RedLib, you can make your own EnchantTrigger:

public class BlockDropTrigger extends EnchantTrigger<BlockDropItemEvent> {
	
	public static final BlockDropTrigger BLOCK_DROP_TRIGGER = new BlockDropTrigger();
	
	private BlockDropTrigger() {}
	
	@Override
	public void register() {
		addListener(BlockDropItemEvent.class, e -> new EventItems(e, e.getPlayer().getInventory().getItemInMainHand()));
	}
	
	@Override
	public boolean defaultAppliesTo(Material material) {
		return material.toString().endsWith("_PICKAXE");
	}
	
}

The register method's purpose is to register any needed listeners which will tell RedLib what items are relevant to the event. You can pass a single item if only one item is relevant, or a "before" and "after" item if the enchantment needs to be activated when something happens and deactivated when something else happens. You can also pass arrays of items instead of individual items for these, in the case that the player's armor or some other set of items is related to the event.

defaultAppliesTo determines the item types that enchantments using this trigger can be expected to apply to. For example, an enchantment using the MINE_BLOCK trigger is most likely going to be applied to pickaxes.

This behavior can also be defined explicitly in the enchantment class, though, by overriding appliesTo. This method is protected by default because it is used to generate an EnumSet for faster access, so to check if a CustomEnchant can be applied to a Material, use canApply(Material). You can also set up incompatibilities with other CustomEnchants by overriding getIncompatible. Currently, this is only called for enchantments using the trigger EQUIP_ARMOR, and is called when a player unequips a piece of armor with the enchantment on it. You can also override the getDisplayName method, though it is recommended to use the namer rather than doing this.

Clone this wiki locally