-
-
Notifications
You must be signed in to change notification settings - Fork 5
Creating a Custom Alchemical Arrow
NOTE: This topic assumes a basic understanding of the Java programming language. Basic concepts will not be reviewed.
In AlchemicalArrows 3, the API was reviewed to make the lives of developers far easier in terms of creating arrows. If you have written custom arrows using the AlchemicalArrows 2 API, you will be familiar with the AlchemicalArrow
class and its properties. It is an abstract class that may be extended, and methods are expected to be overriden to give the arrow its unique characteristics (more on this will be covered in a later wiki page). This wiki will cover the creation of a very simple arrow we will title "Cake Arrow".
First and foremost, a new class must be created. Think of this class like a type declaration for your unique arrow type. This is what defines your arrow. It will hold information unique to your arrow and act as a data class of sorts. By convention, it is recommended to prefix all custom arrow classes with AlchemicalArrow followed by the name of the arrow. In our case, AlchemicalArrowCake
.
public class AlchemicalArrowCake extends AlchemicalArrow {
@Override
public NamespacedKey getKey() {
return null;
}
@Override
public String getDisplayName() {
return null;
}
@Override
public ItemStack getItem() {
return null;
}
}
As you can see, we're prompted with 3 methods that must be overridden: #getKey()
(from the org.bukkit.Keyed interface), #getDisplayName()
and #getItem()
.
#getKey(): The unique identification for your arrow. The use of a NamespacedKey allows for name clashing between plugins. For example, foo:cake
does not necessarily have the same properties as bar:cake
. By convention, the ID should be all lowercase and use underscores in place of spaces. Alphanumeric if possible.
#getDisplayName(): Your arrow's display name. What's returned here should be expected to be displayed in chat. It may be colourized or formatted however you like.
#getItem(): Returns the ItemStack that represents your arrow. The arrow MUST be of type Material#ARROW and must not be null, otherwise, an exception will be thrown upon registration.
Now that we've reviewed what all the methods do, let's quickly populate those methods with values that reflect our arrow. Because a lot of these are constant, it's recommended that these values be created in the constructor and stored as fields. Additionally, due to the nature of Bukkit's NamespacedKey
, a constructor should be created that accepts at least an instance of our plugin. For our case, we'll assume the name of our main class is MyPlugin
public class AlchemicalArrowCake extends AlchemicalArrow {
private final NamespacedKey key;
private final ItemStack item;
public AlchemicalArrowCake(MyPlugin plugin) { // Instance of MyPlugin in constructor. Used for NamespacedKey
this.key = new NamespacedKey(plugin, "cake"); // Arrow ID of "myplugin:cake"
this.item = new ItemStack(Material.ARROW);
// Giving our item a name
ItemMeta meta = item.getItemMeta();
meta.setDisplayName(ChatColor.LIGHT_PURPLE + "Cake Arrow");
this.item.setItemMeta(meta);
}
@Override
public NamespacedKey getKey() {
return key;
}
@Override
public String getDisplayName() {
return ChatColor.LIGHT_PURPLE + "Cake Arrow"; // Or, item.getItemMeta().getDisplayName();
}
@Override
public ItemStack getItem() {
return item.clone(); // NOTE: Keep this ItemStack immutable! It should be cloned!
}
}
With that, it's worth noting something about the value being returned in the getItem()
method. While we do want to create a constant ItemStack, we also want to make sure it's immutable. Unfortunately, in Bukkit, ItemStacks are mutable objects and can be modified with a simple call on the object. In fact, this is done by AlchemicalArrows internally, thus the reason a clone should be returned. Returning a clone ensures that our arrow's ItemStack is not modified under any circumstance.
For a very basic arrow, that's about all we need. Though before our arrow can be used in the game, AlchemicalArrows has to know about it. This is done through the use of the ArrowRegistry
. We can leave our AlchemicalArrowCake
class alone. In order to actually register the arrow, in our onEnable()
method, it's as easy as calling ArrowRegistry
's static #registerCustomArrow(AlchemicalArrow)
method and passing an instance of our arrow's class to it.
public class MyPlugin extends JavaPlugin {
@Override
public void onEnable() {
// Create a new instance of AlchemicalArrowCake
ArrowRegistry.registerCustomArrow(new AlchemicalArrowCake(this));
}
}
Congratulations, you've successfully registered the cake arrow! Really, it's as easy as that.
With that, our new arrow has been created. While it may not be featureful nor all that fun to shoot, we've actually created one and AlchemicalArrows recognizes it both in-world and through commands. However, we still have lots more work to do on our cake arrow before we can say it's finished. We still have to declare its properties (if any), execute additional effects and create a custom arrow entity if necessary. Please see the respective wiki pages for those topics.
- Arrow Properties (recommended)
- Overrideable Methods
- Alchemical Arrow Entities