Skip to content

Custom Arrow Properties

Parker Hawke edited this page Jun 10, 2018 · 1 revision

Custom Arrow Properties

NOTE: This topic assumes a basic understanding of the Java programming language. Basic concepts will not be reviewed.

By reading this page, I am under the impression that you are familiar with arrow properties (See wiki page). If this is the case, you should be aware that the ArrowProperty and PropertyMap classes exist. Otherwise, it is recommended that you read the page before continuing with this wiki.

Adding a New Property

The way the property system was designed is such that you need not register any custom properties through AlchemicalArrows. This was done to ensure that properties are separated per plugin and that arrows are registered and created properly & in order. For this wiki, we will pursue the creation of a custom arrow property not provided by AlchemicalArrows 3, one to determine whether it may be picked up or not. Let's start by introducing the constructor.

public ArrowProperty(NamespacedKey, Class<T>, T)

Remember that this is a type parameterized class where T indicates the expected value type for the property. Because we want it to be a boolean-based property, we will construct it as such. Let's get ourselves a main class to work with first.

public class MyPlugin extends JavaPlugin {

    @Override
    public void onEnable() {
        
    }

}

That's better. And now, we create an instance of ArrowProperty to create our custom property. We want this property to be accessible by other plugins, so it's best if this is a public constant in our main class (or whichever class you chose to make it available). Do note that constants are recommended, but if you wish to make a form of registry to handle your custom properties, you are more than welcome to. Such that the constructor of an AlchemicalArrow implementation can access it, you are welcome to handle properties however you wish.

Let's create that instance. We'll give it the key pickupable, resulting in the namespace myplugin:pickupable.

public class MyPlugin extends JavaPlugin {

    public static final ArrowProperty<Boolean> PICKUPABLE = new ArrowProperty<>(new NamespacedKey(this, "pickupable"), Boolean.TYPE, true);

    @Override
    public void onEnable() {
        // Not necessary, but lets authors know when debugging!
        this.getLogger().info("New property " + PICKUPABLE.getKey() + " created!");
    }

}

That's it. Our new property has been created. If we wanted to create an arrow with this property, it would look a little something like this:

public class MyCustomArrow extends AlchemicalArrow {

    public MyCustomArrow() {
        super.properties.setProperty(MyPlugin.PICKUPABLE, false); // Sets pickupable to false
    }

    // All required methods...

}

Evaluating the Custom Property

Creating an instance of ArrowProperty on its own does absolutely nothing. AlchemicalArrows has no idea what your property does, only that it exists and has a default value for arrows that do not explicitly set it (all default arrows by default). In order to use our property and give it meaning, we'll have to use Bukkit's event listeners and the ArrowRegistry. (You are expected to understand how to use the registry. If you do not, see the wiki page).

Let's start by creating ourselves a listener class for the PlayerPickupArrowEvent:

public class PickupableArrowPropertyListener {

    @EventHandler(priority = EventPriority.LOW) // We want this to be called BEFORE AlchemicalArrows' event
    public void onPickupAlchemicalArrow(PlayerPickupArrowEvent event) {
        ArrowRegistry registry = AlchemicalArrows.getInstance().getArrowRegistry();
        Arrow arrow = event.getArrow();
        if (!registry.isAlchemicalArrow(arrow)) return;

        AlchemicalArrowEntity arrowEntity = registry.getAlchemicalArrow(arrow);
        boolean pickupable = arrowEntity.getImplementation().getProperties().getPropertyValue(MyPlugin.PICKUPABLE);

        event.setCancelled(pickupable);
        arrow.setPickupStatus(pickupable ? PickupStatus.ALLOWED : PickupStatus.DISALLOWED);
    }

}

Our property now has meaning. If the property is set to true, picking it up will always be granted. Otherwise, it will always be denied!

Setting Properties for Existing Arrows

Let's say we want to update the death arrow to set its pickupable status to false because it's just way too powerful to be reused. How would we do that if we don't have access to the properties field? Well, we do. As you saw above, the AlchemicalArrow class has a #getProperties() method which returns the PropertMap instance. Let's get an instance of AlchemicalArrowDeath and update its properties real fast. To do this, we can use ArrowRegistry's static #getCustomArrow(Class<T>) method.

public class MyPlugin extends JavaPlugin {

    public static final ArrowProperty<Boolean> PICKUPABLE = new ArrowProperty<>(new NamespacedKey(this, "pickupable"), Boolean.TYPE, true);

    @Override
    public void onEnable() {
        AlchemicalArrowDeath deathArrow = ArrowRegistry.getCustomArrow(AlchemicalArrowDeath.class);

        /* A more generic approach may be taken if you do not want to explicitly depend on external, non-AlchemicalArrows plugins
         *    AlchemicalArrow arrow = ArrowRegistry.getCustomArrow("otherplugin:custom_arrow");
         */

        deathArrow.getProperties().setProperty(PICKUPABLE, false);
    }

}

Really, it's as easy as that. Now, the Death arrow cannot be picked up by players because its property has been set by our plugin. Congratulations!