Nearby block and changing them

Discussion in 'Plugin Development' started by PHILLIPS_71, May 7, 2013.

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

    PHILLIPS_71

    How would I make it if I had a diamond hoe in my hand it would change all water blocks in a 6 block radius to ice. I have never done anything with nearby entitys/blocks befor so I have no idea how I would do this if you can help me it is much appreciated thanks.
     
  2. Offline

    kiwhen

    Nearby entities is very different from nearby blocks. As far as I know, you cannot just grab nearby blocks in a given radius, you would have to loop through them manually. (Again, as far as I know.)

    This isn't all that complicated, though. Here is an example:
    Code:java
    1. @EventHandler
    2. public void onPlayerMove(PlayerMoveEvent e) {
    3. // Is the player holding a diamond hoe?
    4. if(e.getPlayer().getItemInHand() != null)
    5. if(e.getPlayer().getItemInHand().getType() != Material.DIAMOND_HOE)
    6. return; // No, this is not a diamond hoe
    7.  
    8. // Grab the player's location, rounded block value
    9. Location playerPos = e.getPlayer().getLocation();
    10. // Radius
    11. int r = 3;
    12. // Loop through all blocks within the radius (cube, not sphere)
    13. for(int x = (r * -1); x <= r; x++) {
    14. for(int y = (r * -1); y <= r; y++) {
    15. for(int z = (r * -1); z <= r; z++) {
    16. // Grab the current block
    17. Block b = playerPos.getWorld().getBlockAt(playerPos.getBlockX() + x, playerPos.getBlockY() + y, playerPos.getBlockZ() + z);
    18.  
    19. // Is this water?
    20. if(b.getType() == Material.WATER || b.getType() == Material.STATIONARY_WATER)
    21. b.setType(Material.ICE); // Yes, it is!
    22. }
    23. }
    24. }
    25. }

    I use the PlayerMoveEvent, which tracks player movement. It will fire very often, so I do not really recommend doing this, especially if your servers CPU is weak or otherwise busy. I check if the player is holding an item, and if this item is a diamond hoe. If so, I start 3 for-loops, one for each axiz (X, Y and Z). I set up an integer for each loop, (called x, y and z), which starts with a value equal to the player's current position, minus the given radius. The for loops ends when the coordinate (x, y or z) equals the radius, which means that it would start at the players position minus 3, and end at the players position plus 3.

    I'm using 3 instead of 6, simply because it puts less stress on your server. You should do a test to see if it is capable of keeping up with this.

    If you need it to be in a spherical shape instead of a cube, you have to apply some maths to grab the blocks within range. These simple loops will only create ice cubes. :)
     
    matymare and PHILLIPS_71 like this.
  3. Offline

    PHILLIPS_71

    kiwhen
    Thanks so much just 1 last thing is i want to stop them from using the skill if they are in water i did this and canceled the event but it did not work?

    Code:
                            if ((player.getLocation().getBlock().getTypeId() != 8) ||
                                    (player.getLocation().getBlock().getTypeId() != 9)) {
                                player.sendMessage(ChatColor.AQUA + "You cant vanish in water");
                                e.setCancelled(true);
     
  4. Offline

    kiwhen

    I think you've replied in the wrong thread. :p

    First off, there is a bit of a logical error in your statement. You are using the "or" operator (||) which means that you actually saying:
    "If the block the player's legs is currently occupying is not water or is not stationary water, do the following:"
    Let's say that the block is water. The if-statement would still return true, because while it is water, it is not also stationary water at the same time. In other words, this statement will always be true, even if the player is nowhere near water.

    The one thing that is working excatly the way it's supposed to here, is the cancelling of the event. The only problem is that you are cancelling the PlayerInteractEvent, which means that the player's right click action is not registered by the server. However, a fiery disappearance act is not part of any regular Minecraft routine, it is a custom code that you have placed into the PlayerInteractEvent. Even if the event itself is cancelled, the code within the event will still run.

    In other words, it's completely indifferent whether you cancel the event or not; what you want to do is to stop the code you have typed in there from executing (flames and disappearance). To do this, simply use the return-statement. This will stop your method dead in its tracks. Any code that comes after the return-statement will not run. :)
     
    PHILLIPS_71 likes this.
  5. Offline

    PHILLIPS_71

    kiwhen
    Yeah i was :p

    Now i understand on how to do this but how would i make the y cord only 1 as i want the ice to be only 1 thick, and if i wanted the ice to remove after a certain amount of time i would use a delayed task but i don't know how to get the changed blocks and remove them,
     
  6. Offline

    kiwhen

    I'm not a 100% sure about this, but doesn't ice melt by itself after a while?

    As for the Y-axiz, you can just remove the loop that handles the upward layers, and just run everything on the same Y-coordinate. This for-loop here:
    Code:java
    1. for(int y = (r * -1); y <= r; y++) {
    2. // ...
    3. }
    When removed, you also need to remove the y-field from the method that gets the block:
    Code:java
    1. // Grab the current block
    2. Block b = playerPos.getWorld().getBlockAt(playerPos.getBlockX() + x, playerPos.getBlockY() + y, playerPos.getBlockZ() + z);
    The middle part handles the Y-axis here; just replace that with the layer you want, preferably the player's Y-position minus 1 (the layer you're walking on).
     
  7. Offline

    PHILLIPS_71

    kiwhen
    Ahh ok thanks, but i thought the same that the ice would melt but is does not remove all i know i would need to do is set up a delayed task and get the blocks a then remove them but i'm not 100% sure how to do that also how do i make spheres?
     
  8. Offline

    kiwhen

    It would be a bit of a nightmare to set up for a couple of reasons. For one, it would take an enormous amount of delayed tasks to manually melt each ice block. I'm not actually sure how Bukkit would react to this. More importantly, you would have to make sure that the block changes are "valid". Let's say that another player destroys the ice cubes before they melt, or that the chunk where the iceblock is has been unloaded before it melted. I'm a little bit curious as to what application you're planning on using this stuff, as there surely must be easier ways to accomplish it. :)

    As for spheres; maths aren't really my strong side, to say the least. If I had to, I would probably cheat a little bit. Like this:
    Code:java
    1. // Grab the location of something with rounded coordinate values (no decimals)
    2. Location o = p.getLocation().getBlock().getLocation();
    3.  
    4. // Sphere radius
    5. int r = 5;
    6.  
    7. // Grab cuboid around the origin point
    8. for(int x = (r * -1); x <= r; x++) {
    9. for(int y = (r * -1); y <= r; y++) {
    10. for(int z = (r * -1); z <= r; z++) {
    11. // Grab block
    12. Location b = new Location(o.getWorld(), o.getBlockX() + x, o.getBlockY() + y, o.getBlockZ() + z);
    13.  
    14. // Is this block within the radius of the origin point?
    15.  
    16. if(b.distance(o) > r)
    17. continue; // Outside radius
    18.  
    19. if(b.distance((o)) < r - 1)
    20. continue; // Inside radius - 1
    21.  
    22. // It's within the radius. Do stuff
    23. b.getBlock().setType(Material.GLASS);
    24. }
    25. }
    26. }

    It's not a perfect method, but it does sort of create a sphere of glass blocks. I grab a Location (in this case from a player object called "p"), and make sure I have rounded coordinates. Players do not have that, as they can move freely withing a single blocks space. You do not technically need rounded values for this, but the sphere will look like the work of a drunken hobo with no sense of the arts whatsoever if you operate with decimal values. To round up the values of the player, I simply grab the player's location, grab the block where the location is, and then grab the location of the block itself. I start a another set of three-for loops which you may be familiar with; they will loop through all the blocks within the given radius in a cuboid shape. Same stuff as last time. The only difference is within the loops, where I grab the selected block and check the distance between that and the origin point (the player in this case). If the block is outside the radius, it means that the block is near one of the 8 corners of the cuboid, and thus not part of the sphere we're trying to create. Also, to make the sphere hollow, I remove any blocks that are more than one block on the inside of the radius. Logically, you'd think that you could just type "inside the radius", instead of "inside the radius minus one". You can, but the sphere would in most cases not be completely formed. Blocks in Minecraft are cubes, and cubes simply don't roll that way. There will be slight variations in the distance, and allowing for a distance variation of ~2 blocks will in most cases create a complete sphere around the center. You can run some tests to find a more optimal setting for this if you like. This script will certainly create more than a single layer of glass in some situations.

    Oh, and in case you're not familiar with the "continue"-statement, that's a keyword used to force an on-going loop to skip ahead to the next iteration. Can be used with pretty much any kind of loop. :)
     
Thread Status:
Not open for further replies.

Share This Page