Generating an EARTHQUAKE o.O

Discussion in 'Plugin Development' started by thehutch, Dec 30, 2011.

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

    thehutch

    Basically I am trying to create a "Earthquake" by simply getting all the blocks around a player and then randomly destroying them whilst also injuring players within the radius of the caster.

    Also if possible whilst the earthquake is happening there will be World effects like tnt effects

    Here is the current code which atm does completely nothing but I have been messing around with some stuff, I am just a little stuck on it:

    Code:java
    1.  
    2. public class Earthquake {
    3.  
    4. private int damage;
    5. private float mana;
    6. private float radius;
    7.  
    8. public Earthquake(int damage, float radius, float mana, Player p) {
    9. this.damage = damage;
    10. this.radius = radius;
    11. this.mana = mana;
    12. this.initiateEarthquake(p);
    13. }
    14.  
    15. public void initiateEarthquake(Player p) {
    16.  
    17. double locX = p.getLocation().getX();
    18. double locY = p.getLocation().getY();
    19. double locZ = p.getLocation().getZ();
    20.  
    21. Location startloc = new Location(p.getWorld(), locX, locY, locZ);
    22. Location end = new Location(p.getWorld(), locX+radius, locY+radius, locZ+radius);
    23.  
    24. }
    25.  
    26. private void HeroToThere(Location start, Location end, Player caster) {
    27.  
    28. double locX = caster.getLocation().getX();
    29. double locY = caster.getLocation().getY();
    30. double locZ = caster.getLocation().getZ();
    31.  
    32. HashSet<Block> blocks = new HashSet<Block>();
    33. Random rand = new Random();
    34.  
    35. for(int x=(int)locX ; x<this.radius ; x++) {
    36. for(int y=(int)locY ; y<this.radius ; y++) {
    37. for(int z=(int)locZ ; z<this.radius ; z++) {
    38. blocks.add(caster.getWorld().getBlockAt(x, y, z));
    39. }
    40. }
    41. }
    42.  
    43. }
    44.  
    45. private void checkForPlayers(Location start, Location end, Player caster) {
    46.  
    47. double locX = caster.getLocation().getX();
    48. double locY = caster.getLocation().getY();
    49. double locZ = caster.getLocation().getZ();
    50.  
    51. List<Entity> entities = caster.getNearbyEntities(locX, locY, locZ);
    52. int amount = 0;
    53.  
    54. for(Entity e : entities) {
    55. if (start.distanceSquared(end) > start.distanceSquared(e.getLocation())) {
    56. e.remove();
    57. amount++;
    58. }
    59. }
    60. caster.sendMessage(ChatColor.RED + "You killed " + amount + " entities");
    61. }
    62. }
     
  2. Offline

    ItsHarry

    Okay, so what's the problem?
     
  3. Offline

    thehutch

    I am stuck I have no idea on how to do the damage part to players and also the block destroying and the slow generating of the sphere around the player of world effects
     
  4. Offline

    ItsHarry

    In that case, you should change the question to "How do destroy blocks around the player?" instead
     
  5. Offline

    thehutch

    there is more to destroying blocks in this spell

    any ideas ?

    EDIT by Moderator: merged posts, please use the edit button instead of double posting.
     
    Last edited by a moderator: May 22, 2016
  6. Offline

    user_43347

    This would create a drop of the block and remove the block.
    Code:
    public void explodeBlock(Block b) {
        Material m = b.getType();
        World w = b.getWorld();
        w.dropItemNaturally(m, 1);
        b.setType(Material.AIR);
    }
    This is the same, but it has a 0-5 chance of dropping its block, all are destroyed though.
    Code:
    public void explodeBlock(Block b) {
        World w = b.getWorld();
        Random r = new Random();
        if (r.nextInt(6) == 1) {
              Material m = b.getType();
              w.dropItemNaturally(m, 1);
        }
        b.setType(Material.AIR);
    }
    This could get nearby entities and damage them depending on distance.
    I'm also sure there's a much better way to do this using a equation.
    Code:
    for (Entity e : p.getNearbyEntities(5, 5, 5)) {
         if (e instanceof Player) {
              double d = e.getLocation().distance(p.getLocation());
              if (d <= 3 && d > 0) {
                        d = 16.0; // 8 hearts damage
              } else if (d > 3 && d <= 5) {
                        d = 10.0; // 5 hearts damage
              } else if (d == 0) {
                        d = 20.0; // 10 hearts damage
              }
              Player n = (Player) e;
              n.damage(d.intValue());
         }
    }
     
  7. Offline

    thehutch

    @steaks4uce

    hmm thanks for this it will come in handy although how could I put this in a delay so it doesn't just destroy them instantly?

    Also what about the world effects?
     
  8. Offline

    user_43347

    You would need to make a list of all blocks being destoryed, then do something like this.
    This would pause one second, then destroy the block, and move on to the next in the list.
    Code:
    for (Block b : blocks) { // Given the list is called "blocks"
        plugin.getServer().getScheduler().scheduleSyncDelayedTask(plugin, new Runnable() {
            public void run() {
                explodeBlock(b); // Explode using earlier methods
            }
        }, 10L); // Divide by 20 to get in seconds (this is 1/2 second)
    }
    Now, this would cause extreme lag, it creates a "tnt" effect for every block.
    Code:
    for (Block b : blocks) {
        World w = b.getWorld();
        w.createExplosion(b.getLocation(), 0); // Creates a explosion with no damage
    }
    This is a smoke effect that wouldn't cause nearly as much, but isn't as exciting.
    Code:
    for (Block b : blocks) {
        World w = b.getWorld();
        w.playEffect(b.getLocation(), Effect.SMOKE, 0); // Little smoke effect
    }
    And I would integrate them, like this.
    Code:
    void explodeBlock(Block b) {
        World w = b.getWorld();
        Random r = new Random();
        if (r.nextInt(6) == 1) {
              Material m = b.getType();
              w.dropItemNaturally(m, 1);
        }
        b.setType(Material.AIR);
        w.playEffect(b.getLocation(), Effect.SMOKE, 0);
    }
    Code:
    for (Block b : blocks) { // Given the list is called "blocks"
        plugin.getServer().getScheduler().scheduleSyncDelayedTask(plugin, new Runnable() {
            public void run() {
                explodeBlock(b); // Explode using earlier methods
            }
        }, 10L); // Divide by 20 to get in seconds (this is 1/2 second)
    }
    EDIT by Moderator: merged posts, please use the edit button instead of double posting.
     
    Last edited by a moderator: May 22, 2016
  9. Offline

    thehutch

    I am trying to think of a way to make that dimished damage for greater distance any ideas? This is what I have:

    ((Player) e).setHealth((int) (((Player) e).getHealth() - damage % dist));
     
  10. Offline

    bergerkiller

    @thehutch since I haven't seen it yet, I recommend using some Random in it too. You simply check the surface (using getHighestBlockAt) and randomly pick an x/z value to destroy a block. It's a lot better than busting holes into the ground. :)

    EDIT

    Misread, but maybe picking an x/z is a bit more versatile than using Randoms as a boolean to break a block or not.

    Also,
    The % is not a percentage, use / there. The % is basically a 'remainder return' (modulus) operator.
    Code:
    int a = 5 % 3; //2
    int b = 4 % 3; //3
    int c = 6 % 3; //0
    int d = 8 % 6 //2
     
  11. Offline

    thehutch

    @bergerkiller

    I know what % means :D thats why I used it, I am just simply trying to find the best way to a dimished damage based on how far you are away from the centre. The way I did was basically the greater the player is from the centre the smaller the remainder so less damage :p If you know any better way (more efficient) or more accurate way of doing this then please tell me.
     
  12. Offline

    bergerkiller

    @thehutch well actually not, right now you simply divide the damage value by the distance from the explosion and take the remainder of that. If the damage is 3 and the distance is 0.5, this would return 0.

    Why not use a divide calculation there? Or multiple epicentres?
    Code:
    public double getDamageFactor(double distance) {
        if (distance < 2) return 2.0;
        return 4.0 / distance;
    }
    For example. You could add additional factors or offsets in it too.
     
  13. Offline

    thehutch

    @bergerkiller​
    Ok well this is what I have so far if your wondering :​
    Code:java
    1.  
    Code:java
    1.  
    2. public class Earthquake {
    3.  
    4. private int damage;
    5. private int density;
    6. private int skillLevel;
    7. private float mana;
    8. private float radius;
    9. private float coolDown;
    10.  
    11. public Earthquake(int damage, float radius, float mana, Player p,
    12. int skillLevel, float coolDown, int density) {
    13. this.damage = damage;
    14. this.skillLevel = skillLevel;
    15. this.radius = radius;
    16. this.mana = mana;
    17. this.coolDown = coolDown;
    18. this.density = density;
    19.  
    20. this.initiateEarthquake(p);
    21. }
    22.  
    23. public void initiateEarthquake(Player p) {
    24.  
    25. double locX = p.getLocation().getX();
    26. double locY = p.getLocation().getY();
    27. double locZ = p.getLocation().getZ();
    28.  
    29. Location startloc = new Location(p.getWorld(), locX, locY, locZ);
    30. Location end = new Location(p.getWorld(), locX+radius, locY+radius, locZ+radius);
    31.  
    32.  
    33.  
    34. }
    35.  
    36. private double getDamageFactor(double dist) {
    37. return (double)dist / damage;
    38. }
    39.  
    40. private void destroyBlocks(Block b) {
    41. World w = b.getWorld();
    42. Random r = new Random();
    43. if (r.nextInt(6) == 1 ) {
    44. w.dropItemNaturally(b.getLocation(), new ItemStack(1));
    45. b.setType(Material.AIR);
    46. }
    47. w.playEffect(b.getLocation(), Effect.SMOKE, density);
    48. }
    49.  
    50. private void HeroToThere(Location start, Location end, Player caster) {
    51.  
    52. double locX = caster.getLocation().getX();
    53. double locY = caster.getLocation().getY();
    54. double locZ = caster.getLocation().getZ();
    55.  
    56. HashSet<Block> blocks = new HashSet<Block>();
    57. Random rand = new Random();
    58.  
    59. for(int x=(int)locX ; x<this.radius ; x++) {
    60. for(int y=(int)locY ; y<this.radius ; y++) {
    61. for(int z=(int)locZ ; z<this.radius ; z++) {
    62. blocks.add(caster.getWorld().getBlockAt(x, y, z));
    63. }
    64. }
    65. }
    66. }
    67.  
    68. private void checkForPlayers(Location start, Location end, Player caster) {
    69.  
    70. double locX = caster.getLocation().getX();
    71. double locY = caster.getLocation().getY();
    72. double locZ = caster.getLocation().getZ();
    73.  
    74. List<LivingEntity> le = caster.getWorld().getLivingEntities();
    75.  
    76. for(LivingEntity e : le) {
    77. if (start.distance(end) > start.distance(e.getLocation())) {
    78. e.setHealth((int)(e.getHealth() - this.getDamageFactor(start.distance(e.getLocation()))));
    79. }
    80. }
    81. }
    82. }
    83. [LEFT][/LEFT]
     
  14. Offline

    bergerkiller

    @thehutch
    Code:
    private double getDamageFactor(double dist) {
            return (double)dist / damage;
    }
    That can't be right..so the further away the player, the more damage? Shouldn't it be the other way around?
     
  15. Offline

    thehutch

    So I think I got everything but the expanding circle/box shape around the player? any ideas on how to do this? Since I know you did that crazy chunk stuff where depending on which way you were looking was which chunks loaded first :D
     
  16. Offline

    bergerkiller

    You mean like a block tsunami? You could start off making a single function which moves all top-blocks one up in a rectangular pattern, increasing the radius every x times. This way you will see a wave of blocks rising up.
     
  17. Offline

    thehutch

    You know of any fancy equations which could do so?
     
  18. Offline

    bergerkiller

    @thehutch
    Um I don't have time to write something just now, but I can give some tips:
    - Make one function to raise a block region one up (Block from, Block to, int height)
    - Call this function 4 times (or more) to raise all the blocks.

    But, I guess a circular effect is a lot nicer, for that you'll need to use cosinus/sinus:
    Code:
    public void raiseRadius(Block middle, double radius) {
        Set<Block> toRaise = new HashSet<Block>();
        //go around the circle and find all blocks
        double accuracy = 0.1 * Math.PI;
        int dx, dz;
        for (double d = 0; d < 2 * Math.PI; d += accuracy) {
            dx = (int) (radius * Math.cos(d));
            dz = (int) (radius * Math.sin(d));
            toRaise.add(middle.getRelative(dx, 0, dz);
        }
        raise(toRaise);
    
    }
    public void raise(Set<Block> blocks) {
        for (Block block : blocks) {
            block.getRelative(BlockFace.UP).setTypeAndData(block.getType(), block.getData());
        block.setTypeId(0);
        }
    }
    Something like that. Accuracy needs to be calculated as well, based on the radius used.
     
  19. Offline

    thehutch

    @bergerkiller

    Any idea on how to get a random block from the HashSet? there is no get(); for Sets.
    Could I use a List<Block> instead? Which is fastest?
     
  20. Offline

    bergerkiller

    @thehutch A set is needed, since the loop before it could return the same block twice. And those functions will move ALL blocks in the set one up, that's the idea. The idea is a circular ripple that moves from the middle outwards (radius becomes larger and larger)
     
  21. Offline

    thehutch

    Ok 2 things :D first is does this look right then?

    Code:
    class Earthquake {
    
        private int damage;
        private int density;
        private int skillLevel;
        private float mana;
        private float radius;
        private float coolDown;
        private long tickSpeed;
    
        public Earthquake(int damage, float radius, float mana, Player p,
                          int skillLevel, float coolDown, int density,
                          long tickSpeed) {
    
            this.damage = damage;
            this.skillLevel = skillLevel;
            this.radius = radius;
            this.mana = mana;
            this.coolDown = coolDown;
            this.density = density;
            this.tickSpeed = tickSpeed;
    
            this.initiateEarthquake(p);
        }
    
        public void initiateEarthquake(final Player p) {
    
            double locX = p.getLocation().getX();
            double locY = p.getLocation().getY();
            double locZ = p.getLocation().getZ();
    
            Location startloc = new Location(p.getWorld(), locX, locY, locZ);
            // Location end = new Location(p.getWorld(), locX + radius, locY +
            // radius, locZ + radius);
    
            Bukkit.getScheduler().scheduleSyncRepeatingTask(PluginMain.p, new Runnable() {
    
                public void run() {
                    checkForPlayers(p.getLocation(), p);
                }
            }, 20L, tickSpeed);
            HereToThere(p);
    
        }
    
        /**
         * Returns how much damage the player with take depending on how far they
         * are from the centre
         * @param dist
         * @return double
         */
        private double getDamageFactor(double dist) {
            return (double) damage / dist;
        }
    
        private void explodeBlock(Block b) {
            World w = b.getWorld();
            Random r = new Random();
            if (r.nextInt(6) == 1) {
                Material m = b.getType();
                w.dropItemNaturally(b.getLocation(), new ItemStack(m, 1));
            }
            b.setType(Material.AIR);
            w.playEffect(b.getLocation(), Effect.SMOKE, 0);
        }
    
        /**
         *
         * @param caster
         */
        private void HereToThere(Player caster) {
    
            double locX = caster.getLocation().getX();
            double locY = caster.getLocation().getY();
            double locZ = caster.getLocation().getZ();
    
            Set<Block> blocks = new HashSet<Block>();
    
            this.addBlocks(blocks, caster.getLocation().add(locX, locY-1, locZ).getBlock());
    
            Random rand = new Random();
            int destruction = (int)(Math.PI * (Math.pow(radius, 2))) / 2;
            while(destruction !=0) {
    
                for(final Block b : blocks) {
                    Bukkit.getScheduler().scheduleSyncRepeatingTask(PluginMain.p, new Runnable() {
    
                        public void run() {
                            explodeBlock(b);
                        }
                    }, 0, 10L);
                }
                destruction--;
            }
    
            while (damage != 0) {
                for (final Block b : blocks) {
                    Bukkit.getServer().getScheduler()
                            .scheduleSyncDelayedTask(PluginMain.p, new Runnable() {
                                public void run() {
                                    explodeBlock(b);
                                }
                            }, 100L);
                }
                damage--;
            }
        }
    
    
        private Set<Block> addBlocks(Set<Block> blocks, Block centre) {
    
            double accuracy = 0.1 * Math.PI;
            int dx, dy;
    
            for(double d=0 ; d<2*Math.PI ; d+=accuracy) {
                dx = (int)(radius * Math.cos(d));
                dy = (int)(radius * Math.sin(d));
                blocks.add(centre.getRelative(dx,0,dy));
            }
            raise(blocks);
            return blocks;
        }
    
        private void raise(Set<Block> blocks) {
            for (Block block : blocks) {
                block.getRelative(BlockFace.UP).setType(block.getType());
                block.getRelative(BlockFace.UP).setData(block.getData());
            block.setTypeId(0);
            }
        }
    
        private void checkForPlayers(Location start, Player caster) {
    
            List<LivingEntity> le = caster.getWorld().getLivingEntities();
    
            for (LivingEntity e : le) {
                if (radius > start.distance(e.getLocation())) {
                    e.setHealth((int) (e.getHealth() - this.getDamageFactor(start
                            .distance(e.getLocation()))));
                }
            }
        }
    }
    And secondly would you like to join my team who are creating this plugin, we would very much appreciated with your maths skills :D
     
  22. Offline

    bergerkiller

    @thehutch neh sorry, got more than enough to do already :)
    I just like to throw in my knowledge now and then...

    It looks fine to me, but do test everything correctly, place it under a command or something. When you perform it, a scheduled sync task should update your animation (it is a block animation basically). What needs to happen, is that you update the blocks. But, I'll go ahead and make a function for ya. I feel like it :)

    The function will show a circular ripple extending from under the player.

    http://pastie.org/3102164

    here ya go, this monster will make the epicest earthquake effect ever :)

    EDIT by Moderator: merged posts, please use the edit button instead of double posting.
     
    Last edited by a moderator: May 22, 2016
  23. Offline

    thehutch

    Thank you very much :D Also I edited it a little since I saw you made it in your TrainCarts plugin :D

    Also how do I run it? Do I called the "makeEarthquake" class?
     
  24. Offline

    bergerkiller

  25. Offline

    Tomaz

  26. Offline

    thehutch

    @bergerkiller

    I seem to be having a problem starting the Earthquake, I have it linked upto a playerInteract event and I have debug messages running through and they are all hit but nothing happens :( This is how I call it like you said:

    Code:
                Block epicentre = p.getLocation().add(p.getLocation().getX(), p.getLocation().getY() - 1, p.getLocation().getZ()).getBlock();
                Earthquake e = new Earthquake(epicentre, 10);
                e.makeEarthquake(epicentre, c.getInt("Spells.Earthquake.radius"));
    
     
  27. Offline

    bergerkiller

    @thehutch you are adding the location to the location, use p.getLocation().add(0, -1, 0) instead of doubling the values.
     
  28. Offline

    thehutch

    @bergerkiller

    Ok so I have everything hooked up and I see all the effects and blocks so move but not like you had in your video, in mine I have to spam the spell lots of times, am I meant to for it in a for loop or something? (the makeEarthquake() ) ???
     
  29. Offline

    bergerkiller

    @thehutch it's possible that the area you test it on is not flat enough, you may need to alter the function a bit to make shockwaves pass underneath too. (it's at the while (y < 127) and I go up until I find air, go down there too)

    I'm busy, so can't really help too much...
     
  30. Offline

    thehutch

Thread Status:
Not open for further replies.

Share This Page