Tutorial How to create Damaging Wand Beams

Discussion in 'Resources' started by mine2012craft, Dec 18, 2016.

Thread Status:
Not open for further replies.
  1. Offline

    mine2012craft

    Hello Fellow Bukkit/Spigot Coders,

    Today I decided to take a break from coding and make a small tutorial on how to create Wand beams that go in the player's direction. This beam will go a few blocks, and deal some damage aswell.

    Please keep in mind that this is my first tutorial so I apologize for how long the thread is.

    PLEASE DO NOT SPOONFEED YOURSELF THIS CODE! I want you to understand how this works so that way you can use parts of it in later code!!

    So the first thing we need to do is to create our class. Feel free to name it however you'd like, but in this case I am going to name my class "WandBeam"

    Next, we need to make it a listener. This is because we will be binding this to a stick, and when the player left clicks, the stick shoots the beams, so the server needs to "listen" to see if the stick has been left clicked. Make sure to import the Listener Package for bukkit.

    Your class should now look something like this:

    Code:
    import org.bukkit.event.Listener;
    
    public class <Your Class Name Here> implements Listener {
    
    }
    Now, lets create a public void for our spell. The event that we will be using is the "PlayerAnimationEvent".
    The "PlayerAnimationEvent" is better since it checks if the players hand was swung, and also to make sure the player dosen't casts 2 beams at once.

    Make sure to add the @EventHandler right above it aswell

    Code:
    @EventHandler
    public void beam (PlayerAnimationEvent event){
    
    }
    Next are our conditions. We want to check if the player is holding a stick in their hand, and also if the player has swung their hand with the stick in hand.

    Add these 3 lines to our public void:

    Code:
        if (event.getAnimationType() == PlayerAnimationType.ARM_SWING) { //Checks if the player swung his hand
                 Player player = event.getPlayer(); //Gets the player that caused the animation
                if (player.getInventory().getItemInMainHand() != null){ //Null check
                 if (player.getInventory().getItemInMainHand().getType() == Material.STICK){ //Checks if the player has a stick in their main hand (Anyone earlier than 1.10 needs to use getItemInHand)
          
                 }
             }
    Now that we have that down, lets create the actual spell!

    First, we want this to be instantaneous, so we will be using a for loop that loops 15 times. We will cancel this loop later so lets name this loop:

    Code:
    outerloop: for (double t; t < 16; t+=1){ //Creates a for loop that loops 15 times
    
    } 
    Now, lets add some detail to our spell. We want this to be visible yet awesome, so lets use some particles to create our spell.

    Please keep in mind that I am using the Particle Library that can be found here: https://bukkit.org/threads/1-8-particleeffect-v1-7.154406/

    (https://bukkit.org/threads/1-8-particleeffect-v1-7.154406/page-23 Go here to learn how to update to the new particles)

    But before we can do that, we need to figure out how to make the particles move in a straight line. This can be done simply. We must first find the players Location and direction, and then get their x, y, and z directions:

    Add these 2 lines just above your for loop:

    Code:
                     Location loc = player.getLocation(); // Gets the players location
                     Vector direction = loc.getDirection(); // Gets the direction the player is currently looking at.
                     for(double t = 0; t < 16; t+=1){

    Now lets add the Direction to the location. We will also make it eye level so lets add 1.5 to the y axiz

    Code:
    loc.add(direction);
    loc.add(0,1.5,0);
    
    
    loc.subtract(0,1.5, 0);
    Keep in mind that the loc.subtract will balance how far our beam will go, as without it, it will go further than intended.

    Now that we have those implemented, we can create our beams's damage and visual.

    First, lets imagines that loc.add and loc.subtract are like brackets in code. You can only put stuff there to make it work the way you want it to. That's how our spell works, as anything else that is outside of it will not work the way we intended it to.

    So, lets create our particles and visuals. You can find the link to the particle lib im using above, or use your own. I am going to be using cloud particles for my beam, but you can choose your own if you wish.

    Code:
                      
    loc.add(direction);
    loc.add(0,1.5,0);
              
                         ParticleEffect.CLOUD.display(0, 0, 0, 0.05F, 2, loc, 30);
              
                         loc.subtract(0,1.5,0);
    As you can see, the Particle will be shown at "loc", our created variable, and since our 3 x,y, and z variables are being added to loc, the particle will be spawned at the new loc.

    Lets also add some sound to our spell as well shall we? Choose any sound you want, but I will be using my own sound. Put it at the top of the for loop aswell so it only plays once, as we don't want it to play 15 times.

    Code:
                     player.getLocation().getWorld().playSound(loc, Sound.ENTITY_PLAYER_ATTACK_SWEEP, 1, 2);
                     outerloop: for(double t = 0; t < 16; t+=1){

    Notice I added getWorld() to the line. This will allow all players nearby to hear it. playSound alone will only allow the player to hear it.

    Finally, lets add our damaging effect. We don't want to use "player.getNearbyEntities()" as that will grab the entities around the player only. We need to grab all the entities near the location, and damage them.

    Lets start by creating another for loop that will grab all of the entities within a chunk:

    Code:
                          for (Entity e : loc.getChunk().getEntities()){
    
    }
    Now, let us first check if any entities are within a distance of 1.5 from the beam, and also make sure its not our player who is casting it aswell. We also need to make sure it is alive, and not some droppable item, or else we will get an error.

    Add these 3 conditional lines to our loop:

    Code:
                          for (Entity e : loc.getChunk().getEntities()){
                             if (e.getLocation().distance(loc) < 1.5{ // Checks if any entities are within 1.5 distance
                                 if (e != (player)){    // Checks if the entity is not the user
                                 if(e.getType().isAlive()) {  // Checks that it is a living entity, and not a item or projectile
    
    }
    }
    }
    Now, we must damage the entity that has met all of these 3 conditions. Of course, if to try to use e.damage();, it wouldn't pop up on the menu. We need to cast our entity to a damageable entity, then we can damage it. Lets also make it know that the player was the one who damage our entity.

    Add these 2 new lines to our for loop:

    Code:
    Damageable d  = (Damageable) e; //Casts the entity to a damageable entity
    d.damage(5, player);
    //Damages the entity, and also marks the player as the damager

    Now, lets add some visual effect to when a entity is hit:


    Code:
                         ParticleEffect.CLOUD.display(0, 0, 0, 0.13F, 50, loc, 30);
    Finally, lets stop the beam from going any futher if it hits a entity or a block. We are going to break our named loop. Put that inside the inner loop:

    Code:
    break outerloop;
    Next, above our damaging loop, check if the block that the location is currently in is solid, then break it:

    Code:
        if (loc.getBlock().getType().isSolid()){ //Checks if the block the location is in is solid
                             ParticleEffect.CLOUD.display(0, 0, 0, 0.13F, 50, loc, 30);
                             break outerloop;
                         }
    In the end (Finally...), your public void should look like this:

    Code:
    @EventHandler
        public void beam (PlayerAnimationEvent event){
    
    
             if (event.getAnimationType() == PlayerAnimationType.ARM_SWING) {
                 Player player = event.getPlayer();
                  if (player.getInventory().getItemInMainHand() != null){ 
                 if (player.getInventory().getItemInMainHand().getType() == Material.STICK){
          
                     Location loc = player.getLocation();
                     Vector direction = loc.getDirection();
          
                     player.getLocation().getWorld().playSound(loc, Sound.ENTITY_PLAYER_ATTACK_SWEEP, 1, 2);
                     outerloop: for(double t = 0; t < 16; t+=1){
    
              
                         loc.add(direction);
                         loc.add(0, 1.5, 0);
    
                         ParticleEffect.CLOUD.display(0, 0, 0, 0.05F, 2, loc, 30);
              
                         if (loc.getBlock().getType().isSolid()){
                             ParticleEffect.CLOUD.display(0, 0, 0, 0.13F, 50, loc, 30);
                             break outerloop;
                         }
                  
                          for (Entity e : loc.getChunk().getEntities()){
                             if (e.getLocation().distance(loc) < 1.5){
                                 if (e != (player)){
                                 if(e.getType().isAlive()) {                                     
                                 Damageable d = (Damageable) e;
                                 d.damage(5, player);
                                 ParticleEffect.CLOUD.display(0, 0, 0, 0.13F, 50, loc, 30);
                                 break outerloop;                 
                             }
                         }
                         }
                         }
                   loc.subtract(0, 1.5, 0);
    
                   }
                     }
                 }
             }
        }
    Your beam should look similar to this:

    http://imgur.com/a/JGTQk

    And damage entities like this:

    http://imgur.com/a/DG0P2




    Congratulations! You have made a beam spell! I will be assuming you know how to add it to your Core class.

    With a bit of tweaking, you can make this beam non-instantaneous and have it act like a missile, and even add some cooldowns, and potion effects to hurt entities.

    Please reply if you find any bugs or have any questions or comments.

    Thank you,
    WarlordWeaponry
     
    Last edited: Dec 21, 2016
  2. Offline

    Zombie_Striker

    Neat tutorial. Have not seen one yet about this topic.

    There are a few things that should be fixed.
    You should use == for these. It is convention for enums to be compared using == and you will never run into two instance of the same player, so you can compare memory space instead of checking every single value of the player class.

    Remember to remind people using and version earlier than 1.10 to remove the main part. Also, null check before getting the type. If a player is not holding anything, this will throw an NPE.

    This here is messier than it needs to be. I would recommend doing the following:
    1. Outside of the for loop, create a Location variable. This is where the effect will be spawned. This will be equal to the player's location.
    2. Create a Vector (org.bukkit vector, not the java one) that will be equal to the player's direction, normalized. This means the highest value will be "1".
    3. Inside the for loop, add the vector to the location.
    Using this, you only create two values, and you do not need to subtract the XYZ afterwards. Also with this, you can divide the Vector by 2 if you want the particles to be closer together.

    One final thing about this section, you should specify that the "16" represents the range. Some players may want this to go farther/ be shorter
    For this, this will check if the entity is with 1.5 blocks. In other words, the entity can be almost two blocks away from the stream and they will still be damaged. At the most, I would recommend 0.7 as the range (0.5 would be better)
     
  3. Offline

    mine2012craft

    @Zombie_Striker Alrighty, thanks for the tips. I'm changing it up now. This is actually what I typically do for beams or missiles

    Code:
                 if (player.getInventory().getItemInMainHand().getType() != Material.AIR)
    This might actually fix a null pointer I've been having with a couple of my classes.

    EDIT: Adding the direction to the location doesn't make the beam move, but only sit there.

    Code:
        loc.add(direction);
                         loc.add(0, 1.5, 0);
     
    Last edited: Dec 18, 2016
  4. Offline

    Zombie_Striker

    @mine2012craft are you still subtracting the xyz If so, remove that line
     
  5. Offline

    mine2012craft

    @Zombie_Striker Ah, alright. Fixed in Tutorial. If that's it then thank you.
     
  6. @mine2014craft It work :) But in your code you kill only creature in the chunk , not in the world or in a range :/
     
    Last edited: Dec 29, 2016
  7. Offline

    mine2012craft

    @xFoundation If you want to kill the entity in the world, just change
    Code:
    getChunk().getEntities()
    to
    Code:
    getWorld().getEntities()
    Sorry about that, it was just one of the first methods of damage I learned and I've used it ever since.
     
  8. Offline

    MrGriefer_

    Really nice tutorial. Thank you this helped me a lot!
     
  9. I believe there is also a simple mathematical equation for this as well. Good tutorial though!


    Sent from my iPhone using Tapatalk
     
Thread Status:
Not open for further replies.

Share This Page