Explosive Arrows

Discussion in 'Plugin Development' started by chasechocolate, Sep 20, 2012.

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

    chasechocolate

    I would like some code that will make it so when a player shoots an arrow, it will make a TNT sized explosion where the arrow lands. Thanks!
     
  2. Offline

    MrFigg





    You probably only need the last one, but I included all of them for completions sake.
     
    chasechocolate likes this.
  3. Offline

    chasechocolate

    MrFigg is there one that shows how to code an explosive arrow plugin?
     
  4. Offline

    MrFigg

    ... The last one... Like I said...

    Edit: Also, you seem fairly new to this so I'm pretty sure you'll ask about this eventually. These videos are kinda old and still use the old event system, so you'll have to convert them to the new event system. (http://wiki.bukkit.org/Introduction_to_the_New_Event_System) But the code inside the listener should work fine.
     
    chasechocolate likes this.
  5. Offline

    chasechocolate

    MrFigg can I have link to that video because I can't see the link or title. Thanks!
     
  6. Offline

    MrFigg

    chasechocolate likes this.
  7. Offline

    chasechocolate

    MrFigg thx sorry for bothering, I didn't see it probably because I was in my iPad.
     
  8. Offline

    Sephi

    Code:
     @EventHandler(priority=EventPriority.HIGH)
        public void onProjectileHit(ProjectileHitEvent event) {
          Entity entity = event.getEntity();
          if ((entity instanceof Arrow)) {
            Arrow arrow = (Arrow)entity;
            Entity shooter = arrow.getShooter();
            if ((shooter instanceof Player)) {
              Player player = (Player)shooter;
                player.getWorld().createExplosion(arrow.getLocation(), 5.0F);
            }
          }
        }
    Be advised this will destroy blocks regardless of any protections in place.
     
    chasechocolate likes this.
  9. Offline

    chasechocolate

    Sephi is there a way to not damage blocks, but still give players damage?
     
  10. Offline

    Tirelessly

    0.0f instead of 5.0f
     
  11. Offline

    Sephi

    I've been looking into that myself but I can't for the life of me how to figure out how to replicate exactly a TNT explosion where damage is factored in by the proximity of your location to the initial blast, while leaving the surrounding blocks intact.

    As Tirelessly said, you can negate the explosion power from 5.0F to 0.0F, but that will also negate all damage the surrounding entities receive.
     
  12. Offline

    Jnorr44

    what you do is get all the blocks within the radius of the explosion on the hit event, get the world of the hit event, make the explosion, restore the blocks that have been removed, clear the list/hashmap/hashset/whatever you are using to store blocks.

    Sephi
    there is the possibility of a player going through a portal, at the exact time before this line gets fired, making it so you will actually destroy blocks in the nether/end. To combat this, just do arrow.getWorld(), or event.getWorld()
    player.getWorld().createExplosion(arrow.getLocation(), 5.0F);
     
  13. Offline

    kyle1320

    Or
    Code:
    float size = 4.0F;
    net.minecraft.server.Entity arrow = (net.minecraft.server.Entity)((CraftEntity)entity).getHandle();
    Explosion explosion = new Explosion(arrow.world, arrow, arrow.locX, arrow.locY, arrow.locZ, size);
     
    explosion.a();
    where entity is the arrow
     
  14. Offline

    Jnorr44

    Thats the same exact thing, except it uses NMS code, which is subject to break. I would recommend using my way, because it uses bukkit methods, and Location contains world, x, y, z, pitch, and yaw, so you don't have to get all the info yourself.
     
  15. Offline

    kyle1320

    My way doesn't even damage the blocks, where in your way you have to go back and replace each block, which could potentially leave some things non repaired (chests, signs, etc.). It could break, yes, so it's just preference I guess.

    EDIT: Also with a large enough explosion, replacing each block from a hashmap could get quite laggy.
     
  16. Offline

    Sephi

    Hmm decisions, decisions. I could see Jnorr's method problematic if it blew up a player's worldguarded / faction protected chests full of goodies.. then it simply restores an empty chest, or worse, a chest with the original items and the explosion causing the chest to burst with the items on the ground (thus duplicating the contents of the chest). I know it is possible to store and restore the data inside the chest as well, but it is a bit beyond my league.. still pretty new to this. :(

    Signs, I wouldn't be so concerned about unless it permanently deleted the text.. which it possibly could if all I did was store the block material and restore it after the event... that or I'm just being paranoid.

    Kyle's way I can kind of understand on its own, but my dumbness (or maybe being awake for 30 hours is just making me brain dead at the moment sigh) is preventing me from seeing how it would be used in the listener's Event Handler..

    I'll be doing even more research to try and understand both of these ways. Sleep might be a healthy option too! haha
     
    W1cked1 likes this.
  17. Offline

    Jnorr44

    kyle1320 You can use BlockStates, which are very fast, because your just getting info on the blocks. Problem solved :D
     
  18. Offline

    Icyene

    OR, you could do EntityExplodeEvent.blockList().remove(yourBlock). Because you are not storing the value of blockList in a local variable, blockList just passes a reference of the ArrayList<Block> in EntityExplodeEvent. You can clear it completely by .blockList().clear(), if you want to completely disable it. Its a nicer way of the reflection equivalent:

    Code:Java
    1.  
    2. Field blockList = EntityExplodeEvent.class.getDeclaredField("blockList");
    3. ......
    4. ......
    5. <in your event handler, where e is the EntityExplodeEvent>
    6. blockList.set(e, ((List)blockList.get(e)).remove(yourBlock));
    7.  


    I do not advise using the above code. Just use the reference.

    Jnorr44 Using this method, the chest wouldn't even be removed, so you wouldn't have to deal with storing the contents of the chest and readding them. More efficient, too.
     
  19. Offline

    MrFigg

    Umm... yea. Making an explosion that deals full entity damage without doing block damage is a lot easier than ether Jnorr44 or kyle1320s suggestions. Just use the EntityExplodeEvent Icyeye mentioned. Though you don't even need to get that complicated. Just do something like this;
    Code:JAVA
    1. public class ExplodingArrow extends JavaPlugin implements Listener {
    2. private boolean clearBlocks = false;
    3.  
    4. public void onEnable() {
    5. getServer().getPluginManager().registerEvents(this, this);
    6. }
    7.  
    8. @EventHandler(priority=EventPriority.HIGHEST)
    9. public void onProjectileHit(ProjectileHitEvent event) {
    10. Entity entity = event.getEntity();
    11. if((entity instanceof Arrow)) {
    12. Arrow arrow = (Arrow) entity;
    13. Entity shooter = arrow.getShooter();
    14. if((shooter instanceof Player)) {
    15. noBlockExplosion(arrow.getLocation(), 5.0F);
    16. arrow.remove();
    17. }
    18. }
    19. }
    20.  
    21. public void noBlockExplosion(Location location, float force) {
    22. clearBlocks = true;
    23. location.getWorld().createExplosion(location, force);
    24. clearBlocks = false;
    25. }
    26.  
    27. @EventHandler(priority=EventPriority.HIGHEST)
    28. public void onEntityExplode(EntityExplodeEvent event) {
    29. if(clearBlocks) event.blockList().clear();
    30. }
    31. }

    I know it's kinda ugly having a boolean used like that but it's quite effective. I'd just put it, the explosion listener, and the noBlockExlposion method in it's own class to keep it out of sight.
     
    Ibix13 and Icyene like this.
  20. Offline

    Sephi

    MrFigg This idea works perfectly fine.

    Icyene This idea also works great, though seems more efficient, and as MrFigg admitted, using the boolean as he did wouldn't exactly be encourage despite it having full functionality.

    I think I will use Icyene's method, thank you so much to everyone who helped. I hope this helps the topic creator here, as I will mark my thread (that was created before this one, but basically asking the same thing) as [Solved].

    http://forums.bukkit.org/threads/solved-onprojectilehit.101715/
     
    W1cked1 likes this.
  21. Offline

    chasechocolate

  22. Offline

    Sephi

    chasechocolate If you don't like messing around with the complexity of Lists (though once you understand them they really aren't that complex), I recommend using MrFigg's code as at the very least it functions just as effectively as he states it does, both ways were tested.
     
  23. Offline

    Icyene

    Sephi NMS is much more complicated than lists. And will have to be updated when a() is renamed.

    MrFigg Thas not very safe... You should store the id of the arrow and boolean in a Hashmap or Tuple. And have a list of those.

    EDIT by Moderator: merged posts, please use the edit button instead of double posting.
     
    Last edited by a moderator: May 28, 2016
  24. Not true.
    It calls this code:
    https://github.com/Bukkit/CraftBukk...a/org/bukkit/craftbukkit/CraftWorld.java#L440
    Which calls this code:
    https://github.com/Bukkit/CraftBukk...in/java/net/minecraft/server/World.java#L1519
    Now look for yourself was explosion.a() and explosion.a(true); does: It fires all needed events for protection plugins.
    https://github.com/Bukkit/CraftBukkit/blob/master/src/main/java/net/minecraft/server/Explosion.java
    :)

    For a prove I compiled this:
    Code:java
    1. public class ExplosiveArrows extends JavaPlugin implements Listener
    2. {
    3. public void onEnable()
    4. {
    5. getServer().getPluginManager().registerEvents(this, this);
    6. }
    7.  
    8. @EventHandler(priority=EventPriority.HIGH)
    9. public void onProjectileHit(ProjectileHitEvent event)
    10. {
    11. Entity entity = event.getEntity();
    12. if(entity instanceof Arrow && ((Arrow)entity).getShooter() instanceof Player)
    13. entity.getWorld().createExplosion(entity.getLocation(), 5.0F);
    14. }
    15. }

    (which is basically the same, just simplified) and used it together with BananaRegion:

    (needs some time to upload/convert).
     
    chasechocolate and Icyene like this.
  25. Offline

    MrFigg

    Very true, I would never actually use that as is. Just a proof of concept.
     
  26. As long as minecraft is single-threaded this is perfectly safe as it's impossible that another ExplosionEvent gets fired while the boolean is true.
     
Thread Status:
Not open for further replies.

Share This Page