[SOLVED] On how to get the block an arrow lands in

Discussion in 'Plugin Development' started by kaiser_czar, Jan 20, 2012.

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

    kaiser_czar

    So, I have the desire for blocks, specifically glass, to break when hit with an arrow. However, I am not sure how to get the actual block it hits; getting the location of the arrow returns the air block in which the majority of the arrow is still sticking out.

    Step 1: Get the arrow's location after the ProjectileHitEvent.

    At first, I tried a BlockIterator initialized with the arrow's location. This turned out some seriously weird blocks that were often nowhere near the arrow. So, I abandoned this approach.

    Secondly, I considered trying an approach based on the arrow's pitch and yaw. However, I realized that a single pitch and yaw could put the arrow in relation to a whole bunch of different blocks; 5 to be exact. So that was scrapped also.

    The only idea I have left is comparing the type double coordinates to the type int blockCoordinates of the arrow. Using these, find the closest block.

    So, your opinions. Will the last one even work? Is there an easier way to do this?

    EDIT: The last option does the same thing as the BlockIterator and tends to return somewhat random positions. I'm now at a full loss on how to do this.
     
  2. Offline

    Rathfon

    Unfortunately, it's very late and I won't have many good answers, and I don't mean to bump this thread.

    But, the location the when the arrow hits that it will return is unfortunately the block the arrow is "inside" as in: not the block it's actually "piercing", but actually the block it inhabits.

    Depending on the angle the arrow is fired from, this may be difficult to determine. I attempted this before, trying to find out if I could grab pitch/yaw and check, but that's way too much of a wildcard.

    I even tried checking every block around the block the arrow was 'in', and doing it accordingly, but until they come up with a getAngle on the arrow we are screwed, because even then, the angle will only be accurate 50% of the time.
     
  3. Offline

    AmoebaMan

    One liner:
    Code:
    event.getProjectile().getWorld().getBlockAt(event.getProjectile().getLocation()).setType(Material.AIR);
    
    Of course, this doesn't include the check to see if it's the right type of block (glass, etc.), but this will change the impact block to air.
     
  4. Offline

    Father Of Time

    I found that block iterators do this well. Get the direction vector from the arrow (it keeps its direction when it impacts an object) and use that to make a vector of the direction the arrow is facing, then simply do a block interator that traces from the arrows location in the direction the arrow is facing for 1 block distance, what ever it collides with is your impact block.

    I was part of a long conversation on this subject awhile ago where people tried many things, and if I remember correctly this was the solution that ultimately gave the "best" results.

    Hope this helps!
     
  5. Offline

    kaiser_czar

    Yea, I considered the pitch/yaw idea already. Problem is, you can have a single set of pitch/yaw and still have multiple blocks it could be in.

    This was obviously the first thing tried. Problem is the arrow is not inside the block it lands on.

    Aye, considered this also; this method produced the weirdest and most unpredictable results. As a test, I had it change whatever was first along the BlockIterator to bedrock, so I could see. Never once did it change the block the arrow landed in; sometimes, it even hit stuff 2-3 blocks away. I'm utterly befuzzled as to how that could have happened.
     
  6. Offline

    bergerkiller

    kaiser_czar you really should start looking at the native coding.

    You know for a fact that the server knows the block is there. What block? This is done using collision testing.
    Collisions are calculated in the physics function, thus we look at the physics function:
    https://github.com/Bukkit/CraftBukk...ava/net/minecraft/server/EntityArrow.java#L94

    You can see that e, f and g stand for the block x y and z the arrow is currently in. Note the 'isInGround' property, which you can use to check if it collided with the ground.

    You'll need to use reflection to obtain the block position. (make a new field("e" - g), get on the entity arrow)
     
    mushroomhostage likes this.
  7. Offline

    mushroomhostage

    This works:

    Code:
                World world = arrow.getWorld();
    
                net.minecraft.server.EntityArrow entityArrow = ((CraftArrow)arrow).getHandle();
    
                Field fieldX = net.minecraft.server.EntityArrow.class.getDeclaredField("e");
                Field fieldY = net.minecraft.server.EntityArrow.class.getDeclaredField("f");
                Field fieldZ = net.minecraft.server.EntityArrow.class.getDeclaredField("g");
    
                fieldX.setAccessible(true);
                fieldY.setAccessible(true);
                fieldZ.setAccessible(true);
    
                int x = fieldX.getInt(entityArrow);
                int y = fieldY.getInt(entityArrow);
                int z = fieldZ.getInt(entityArrow);
    
                Block block = world.getBlockAt(x, y, z);
    
    Note that if you're calling it from a ProjectileHitEvent handler, all the fields will be -1 because of when CraftBukkit calls the event. But you can use Bukkit.getScheduler().scheduleSyncDelayedTask() (with no delay) in your handler to call a Runnable and it'll be able to get the correct location:

    [​IMG]
     
    Stevenpcc, Alvarez96 and kaiser_czar like this.
  8. Offline

    kaiser_czar

    mushroomhostage That works absolutely beautifully. Thank you very much!
     
  9. Offline

    mushroomhostage

    Has anyone figured out how to get this to work for all projectiles? It appears only EntityArrow does the block collision detection, presumably so it can know what block to stick the arrow into, but other projectiles such as EntitySnowball just poof on hit, apparently not calculating the block collision.
     
  10. Offline

    Kevin Forte

    Sorry to bump this, but could you possible post a tutorial of sorts or post the source of the plugin you used to make that image? I really need that code.
     
  11. Offline

    Baba43

    mushroomhostage

    What files must be included to get that code to work?

    Or is this idea outdated?
     
  12. Offline

    kalicat

    main class derp
     
  13. Offline

    Stevenpcc

    I had to tackle this problem tonight. I took mushroomhostage's technique, modified it to work with the latest bukkit, and made it into a plugin that fires an event when an arrow hits a block. The event will provide the arrow and the block that it hit. Works like this

    Code:java
    1. @EventHandler
    2. private void onArrowHitBlock(ArrowHitBlockEvent event) {
    3. Arrow arrow = event.getArrow();
    4. Block block = event.getBlock(); // the block that was hit
    5. }


    Source code is available here: https://github.com/stevenpcc/ArrowHitBlockEvent

    The jar is here: http://dev.bukkit.org/bukkit-plugins/arrowhitblockevent/
     
Thread Status:
Not open for further replies.

Share This Page