Creating an Alchemical Arrow

Jun 1, 2016
Creating an Alchemical Arrow
  • Creating an Alchemical Arrow

    Since the previous iteration of AlchemicalArrows, it was impossible to allow for the creation of externalized arrows without the need to use reflection, modifying a privated enumeration, and a couple of other methods and switch statements built-in to the code (impossible). AlchemicalArrows 2 was written with developers in mind. It's much easier to create arrows, as all methods are pre-built into a class, and registered upon launch for you. There's no need for you to listen for a ProjectileLaunchEvent.



    AlchemicalArrow Class


    First thing's first, create a new class. It is recommended that this class should be named as <Your_Arrow_Name>Arrow (ex: AirArrow). This is not required, but it is suggested as the name of the arrow is based on the name of the class. We will be creating an arrow known as "TestArrow" for this example. Your class should look like so:
    Code (Java):
    public class TestArrow{
     
    }
    In AlchemicalArrows 2, creating a new arrow is as simple as extending an abstract class. You're going to want to make your new "TestArrow" class extend AlchemicalArrow. Not to be confused with AlchemicalArrows (the main AlchemicalArrows class). Your class should now look like this

    Code (Java):
    public class TestArrow extends AlchemicalArrow{
        public TestArrow(Arrow arrow){
            super(arrow);
        }
    }
    You have now created your first AlchemicalArrow! Granted, your arrow won't do a whole heck of a lot, but let's fix that.


    Displaying Particle Effects

    As of now with your TestArrow class, it's nothing more than a regular arrow. It has no special capabilities nor any particle effects to display whilst its alive. In order to do that, we must take advantage of the newly extended AlchemicalArrow class. Let's make our TestArrow display the new 1.9 SWEEP_ATTACK particle. To do that, we're going to override a method from the AlchemicalArrow class, "displayParticle(Player)", like so

    Code (Java):
    public class TestArrow extends AlchemicalArrow{
        public TestArrow(Arrow arrow){
            super(arrow);
        }

        @Override
        public void displayParticle(Player player){
         
        }
    }
    This method allows you to display particles to all players whilst the arrow is still alive. After the arrows death, this method will no longer be called. Let's just write a quick little bit of particle code. Your class should look something like this

    Code (Java):
    public class TestArrow extends AlchemicalArrow{
        public TestArrow(Arrow arrow){
            super(arrow);
        }

        @Override
        public void displayParticle(Player player){
            player.spawnParticle(Particle.SWEEP_ATTACK, getArrow().getLocation(), 3, 0.1, 0.1, 0.1, 0.1);
        }
    }
    There we go! Our arrow will now display the sweep particle to all players within a radius of 20 when it's in the air. Feel free to add varying particles, or particles to specific players, it's really up to you. You have to note that the player parameter for this method is actually all players within a range of 20 blocks from the arrow.


    Effects


    Now your arrows display particles. But that's pretty much it. How do you make them actually do things? It's a lot easier than you think. Much like the displayParticle() method, you're going to override another couple of methods. Note that when doing this, there are three methods you're able to override. One called when the arrow hits the player, another called when hitting any other entity, and lastly a method called when hitting a block.

    On top of that, there are also some methods to handle event-specific methods in which you can cancel events, though I recommend staying away from those unless absolutely necessary. I will not exemplify them in this tutorial for that reason. If you would like to use them, you are free to look at the AlchemicalArrow.class source code under the me.choco.arrows.api package


    When Hitting Players


    Let's say we want to make the arrow explode after it hits a player. It's as easy as overriding a super method called "onHitPlayer()". Your class would look a little something like this

    Code (Java):
    public class TestArrow extends AlchemicalArrow{
        public TestArrow(Arrow arrow){
            super(arrow);
        }

        @Override
        public void displayParticle(Player player){
            player.spawnParticle(Particle.SWEEP_ATTACK, getArrow().getLocation(), 3, 0.1, 0.1, 0.1, 0.1);
        }

        @Override
        public void onHitPlayer(Player player){
         
        }
    }
    Now we have our method which is called every time the arrow hits a player. Let's make the arrow explode, and see if we can do some damage! Let's just quickly add an explosion method into here. Just some basic Bukkit API methods

    Code (Java):

    public class TestArrow extends AlchemicalArrow{
        public TestArrow(Arrow arrow){
            super(arrow);
        }

        @Override
        public void displayParticle(Player player){
            player.spawnParticle(Particle.SWEEP_ATTACK, getArrow().getLocation(), 3, 0.1, 0.1, 0.1, 0.1);
        }

        @Override
        public void onHitPlayer(Player player){
            player.getWorld().createExplosion(player.getLocation(), 3);
        }
    }
     
    It was as easy as that. As soon as a player's hit, it will explode!


    When Hitting Entities

    Much like the above method for onHitPlayer(), it's just a method you need to override. It works exactly as onHitPlayer(), but instead of specifying Players, this will fire when any type of entity is hit excluding Player. Let's say we want the arrow to explode with a larger power and set the ground on fire when it hits any other entity. The method we have to override this time is onHitEntity(Entity).

    Code (Java):
    public class TestArrow extends AlchemicalArrow{
        public TestArrow(Arrow arrow){
            super(arrow);
        }

        @Override
        public void displayParticle(Player player){
            player.spawnParticle(Particle.SWEEP_ATTACK, getArrow().getLocation(), 3, 0.1, 0.1, 0.1, 0.1);
        }

        @Override
        public void onHitPlayer(Player player){
            player.getWorld().createExplosion(player.getLocation(), 3);
        }

        @Override
        public void onHitEntity(Entity entity){
            entity.getWorld().createExplosion(entity.getLocation(), 5, true);
        }
    }
    Our arrow will now explode when it hits an entity. Simple as that.


    When Hitting the Ground

    Last but not least, we have the ability to add an effect when the arrow hits a block. This was not possible in AlchemicalArrows 1, but with this new API, we are able to determine what block it hits and modify it. Much like the above two effect methods, we're going to be overriding the onHitBlock(Block) method. This time, let's make the block disappear when it hits the ground

    Code (Java):
    public class TestArrow extends AlchemicalArrow{
        public TestArrow(Arrow arrow){
            super(arrow);
        }

        @Override
        public void displayParticle(Player player){
            player.spawnParticle(Particle.SWEEP_ATTACK, getArrow().getLocation(), 3, 0.1, 0.1, 0.1, 0.1);
        }

        @Override
        public void onHitPlayer(Player player){
            player.getWorld().createExplosion(player.getLocation(), 3);
        }

        @Override
        public void onHitEntity(Entity entity){
            entity.getWorld().createExplosion(entity.getLocation(), 5, true);
        }
     
        @Override
        public void onHitGround(Block block){
            block.setType(Material.AIR);
        }
    }
    All done! Now our arrow should sort of tunnel its way down to bedrock, which is pretty neat. That's really all we want to do for our events.


    On Shot from Entities

    On top of all of those amazing features we can do with effects when the arrow hits something, we can even add an effect to when it's shot! As of now, only Players and Skeletons are supported when it comes to being shot, though BlockProjectileSources are still available to be programmed and will be supported soon. Let's make it so that when we shoot our arrow, we double it's speed, and when a skeleton shoots it, we half its speed.

    Code (Java):
    public class TestArrow extends AlchemicalArrow{
        public TestArrow(Arrow arrow){
            super(arrow);
        }

        @Override
        public void displayParticle(Player player){
            player.spawnParticle(Particle.SWEEP_ATTACK, getArrow().getLocation(), 3, 0.1, 0.1, 0.1, 0.1);
        }

        @Override
        public void onHitPlayer(Player player){
            player.getWorld().createExplosion(player.getLocation(), 3);
        }

        @Override
        public void onHitEntity(Entity entity){
            entity.getWorld().createExplosion(entity.getLocation(), 5, true);
        }
     
        @Override
        public void onHitGround(Block block){
            block.setType(Material.AIR);
        }

        @Override
        public void onShootFromPlayer(Player player){
            getArrow().setVelocity(getArrow().getVelocity().multiply(2));
        }
     
        @Override
        public void onShootFromSkeleton(Skeleton skeleton){
            getArrow().setVelocity(getArrow().getVelocity().divide(2));
        }
    }
    And there we go! We've pretty much finished all the effects that our arrow can possibly have! Again, there is a onShootFromBlockSource method, though that's unused as of now and will be supported in the future versions of AlchemicalArrows.


    Arrow Boolean Flags


    As you may have noticed, shooting our amazing arrow may be quite overpowered when shot from an infinity bow. Now by default, the Infinity bow enchantment does not allow players to shoot any type of alchemical arrows (including our new add-on arrow). But you know what? Our arrow is super overpowered and we want to make it so amazingly overpowered to the point where we're allowed to use infinity! Let's do that by overriding the boolean-return method allowInfinity()

    Also, while we're at it, since our arrow is so overpowered... we should probably remove the ability for skeletons to shoot it. We don't want to kill all the players on the server. It's as easy as, can you guess? Overriding another method. The boolean-return skeletonsCanShoot() method which defaults to true.

    Code (Java):
    public class TestArrow extends AlchemicalArrow{
        public TestArrow(Arrow arrow){
            super(arrow);
        }

        @Override
        public void displayParticle(Player player){
            player.spawnParticle(Particle.SWEEP_ATTACK, getArrow().getLocation(), 3, 0.1, 0.1, 0.1, 0.1);
        }

        @Override
        public void onHitPlayer(Player player){
            player.getWorld().createExplosion(player.getLocation(), 3);
        }

        @Override
        public void onHitEntity(Entity entity){
            entity.getWorld().createExplosion(entity.getLocation(), 5, true);
        }
     
        @Override
        public void onHitGround(Block block){
            block.setType(Material.AIR);
        }

        @Override
        public void onShootFromPlayer(Player player){
            getArrow().setVelocity(getArrow().getVelocity().multiply(2));
        }
     
        @Override
        public void onShootFromSkeleton(Skeleton skeleton){
            getArrow().setVelocity(getArrow().getVelocity().divide(2));
        }
     
        @Override
        public boolean allowInfinity(){
            return true;
        }

        @Override
        public boolean skeletonsCanShoot(){
            return false;
        }
    }
    Now skeletons can't shoot our arrows, but players are allowed to shoot it if the infinity enchantment is present on their bow! Pretty neat, huh?


    Registering the Arrow

    I think we've finally finished our arrow! I'm quite satisfied with this Test Arrow. You may be asking yourself, "Is that it, Choco?". Well... not quite. There's still one more step until we can try this arrow in game. We have to register it in the onEnable method! This is how AlchemicalArrows knows about your arrow and knows when to register it and run your custom effects. Don't worry! It's really easy. We'll go over it in steps. First, let's head over to our main class with our onEnable method, and work from there.

    Code (Java):
    public class AlchemicalArrowsExtension extends JavaPlugin{
        @Override
        public void onEnable(){
         
        }
    }
    So we have our onEnable() method. It's quite easy from here, really. All you really have to remember is one line from AlchemicalArrows! It requires a couple of parameters though. It should look something like this.

    Code (Java):
    public class AlchemicalArrowsExtension extends JavaPlugin{
        @Override
        public void onEnable(){
            ArrowRegistry.registerAlchemicalArrow(ItemStack, Class<? extends AlchemicalArrow>);
        }
    }
    Although that may seem confusing, it's a lot simpler than one may make it out to be. There are Javadoc comments to explain what to do if you get confused, but if you prefer a visual, continue along. The first parameter, as you can see, requires an ItemStack, and the second one requires a Class that extends AlchemicalArrow.

    The ItemStack parameter is the material that represents our AlchemicalArrow and what needs to be in the players inventory in order to shoot the arrow. There are limitations however. Your ItemStack MUST be of Material ARROW. No, you cannot have TIPPED_ARROW (for now). So let's make the ItemStack we want to represent our arrow in the onEnable() method. You can do this in any other method if you really want to keep your onEnable() clean, as long as the ItemStack parameter is valid.

    Code (Java):
    public class AlchemicalArrowsExtension extends JavaPlugin{
        @Override
        public void onEnable(){
            ItemStack item = new ItemStack(Material.ARROW);
            ItemMeta meta = item.getItemMeta();
            meta.setDisplayName(ChatColor.LIGHT_PURPLE + "Test Arrow");
            item.setItemMeta(meta);

            ArrowRegistry.registerAlchemicalArrow(item, Class<? extends AlchemicalArrow>);
        }
    }
    Now we have our ItemStack registered, but what about that scary Class parameter!? "What do I do with that!?". All you have to do is specify the class you used to create your custom arrow. In this case, it was TestArrow.class. That's all you need to put!! Your onEnable() should now look something like this

    Code (Java):
    public class AlchemicalArrowsExtension extends JavaPlugin{
        @Override
        public void onEnable(){
            ItemStack item = new ItemStack(Material.ARROW);
            ItemMeta meta = item.getItemMeta();
            meta.setDisplayName(ChatColor.LIGHT_PURPLE + "Test Arrow");
            item.setItemMeta(meta);

            ArrowRegistry.registerAlchemicalArrow(item, TestArrow.class);
        }
    }
    We're all done! Our arrow is now registered! If you upload this onto a server with AlchemicalArrows installed, your custom arrow will available. The most amazing thing about AlchemicalArrows 2, is the fact that the "/givearrow" and "/aa killallarrows" commands actually integrate your new arrow. To give yourself one of your newly created arrows, all you're going to have to do is run "/givearrow test" in game, and there's your new arrow. (Remember when I said the name of your class was important?)

    Congratulations! You've just made your first arrow. Here's what it looks like!
    [​IMG]
  • Loading...
  • Loading...