Minecraft Modding: Custom Enchantments

Background


An Enchantment class is associated with an NBT tag that can be applied to items through mechanisms like an enchanting table, an anvil using an enchanted book, and obtained through methods like villager merchant trades and fishing. The NBT tag simply contains the enchantment registry name as well as an enchantment level. The custom enchantment class controls things like:
  • Rarity
  • Min and max levels
  • Whether the enchantment should be considered a "curse"
  • Enchantments can also be combined and canApplyTogether() method controls the valid combinations.
  • Enchantments can be enabled on specific item types according to the canApply() method.
  • Commonly enchantments affect damage amounts so there are some methods related to adding modifiers which are called automatically on enchanted weapons and armor. These methods are onEntityDamaged(), onUserHurt(), and calcModifierDamage().

Key Point: If your custom enchantment will have a typical effect (such as modify damage) then you can simply @Override the appropriate methods, but otherwise you will need to use events to check for the presence of an Enchantment and take appropriate action.

The steps to make a custom enchantment are:
  1. Create a class that extends Enchantment@Override the appropriate methods for rarity, level range, compatibility, etc.
  2. Register the enchantment. Use a handler method for RegistryEvent.Register event along with the @ObjectHolder injection method to instantiate and register the enchantment singleton.
  3. Process enchantment in relevant event. If your enchantment is more than a simple damage modifier you will need to handle some other event and run appropriate code. For example, if you want the enchantment to do something when the player is struck by lightning then you would handle the EntityStruckByLightning event. 
  4. Apply the enchantment. You can let your enchantment simply be randomly generated, but perhaps your mod applies the enchantment in specific ways.
  5. Update your .lang file. You should put a localization key in your .lang file for your enchantment.
Tip: If you want your enchantment to have a continuous effect, such as health regeneration, you would handle the PlayerTickEvent.

Example: "Safe Falling" Enchantment


In each sub-section below I link to some example code. In my example, I've created an enchantment for "safe falling". This is intended to entirely eliminate fall damage (not just reduce it like the vanilla Feather Falling enchantment).

Create A Class That Extends Enchantment


Pretty simple. Just make sure that you look at all the parent methods in Enchantment and @Override the ones that make sense for your enchantment. For example, if you only want your enchantment to be possible on a specific type of item you should @Override the canApply() method to return true if the item matches.

See example code.

Register Your Enchantment


You should use the modern registry event method. So create an event subscribing class with method that handles the RegistryEvent.Register event. I also suggest using the @ObjectHolder method for injecting the registered instance into a static field.

Warning: Make sure you call setRegistryName() somewhere before passing the instance to the register() method. You can call it either in your enchantment class constructor or call it explicitly at the time of registration.

Important: If you use the @ObjectHolder method make sure your field name matches the registry name.

See example code.

Handle An Event With Code For The Enchantment Effect


Depending on your enchantment you should handle a relevant effect. Here are some examples:

  • If you want something that has a continuous effect, such as regeneration, handle the PlayerTickEvent.
  • If the enchantment will affect what you harvest from blocks, handle the HarvestDropsEvent.
  • If the enchantment will make you jump higher, handle the PlayerJumpEvent.
When you handle the event, you will need check whether the custom enchantment is active on an item in use. Enchantments are contained in a dedicated NBTTagList which you need to check. This is actually a bit tricky because:
  1. You need to grab an Iterable list of item stacks in the right place in the player inventory. For example, if your enchantment is related to wearing a certain piece of armor, you'd start with the EntityPlayer#getArmorIventoryList(). But if your enchantment should activate when the item is wielded then you would have to check getHeldEquipment().
  2. You need to iterate through that list checking each stack for possible presence of your enchantment.
  3. Then process the code for your enchantment effect.
See example code (search for LivingFallEvent).

Apply Your Enchantment


Your enchantment is only useful if it is applied. Simply by being registered, it should be possibly applied to loot via the EnchantRandomly#apply() method, or equipped on skeletons and such.

However, you may also need to apply it directly and can do so to any ItemStack using the addEnchantment() method. For example you might use this when you:

  • Create merchant recipes that specifically return items with your enchantment. See example code.
  • Create some very mod-specific way to get the enchantment, like perhaps you have to interact with some special tile entity.


Update Your .lang File


To support language localization, in your .lang resource asset file you should add a key for your asset. The key name will be "enchantment." plus the unlocalized name of your enchantment.

See example code.

Testing


Always test your code thoroughly. At a minimum I suggest you create a world in creative mode that allows cheats then:

  1. Use the /enchant command to check that only appropriate item types can have the enchantment applied.
  2. Use the /enchant command to check that only appropriate combinations of enchantments work with your enchantment.
  3. With an enchanted item in appropriate location in your player inventory, check that the enchantment works as intended.

Conclusion


Custom enchantments are not too hard. The trickiest part is probably the event handling as you need to do some iteration to detect the presence of the enchantment and then code the actual effect.

As always feel free give comments and corrections. Happy modding!

No comments:

Post a Comment