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 public class Earthquake { private int damage; private float mana; private float radius; public Earthquake(int damage, float radius, float mana, Player p) { this.damage = damage; this.radius = radius; this.mana = mana; this.initiateEarthquake(p); } public void initiateEarthquake(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); } private void HeroToThere(Location start, Location end, Player caster) { double locX = caster.getLocation().getX(); double locY = caster.getLocation().getY(); double locZ = caster.getLocation().getZ(); HashSet<Block> blocks = new HashSet<Block>(); Random rand = new Random(); for(int x=(int)locX ; x<this.radius ; x++) { for(int y=(int)locY ; y<this.radius ; y++) { for(int z=(int)locZ ; z<this.radius ; z++) { blocks.add(caster.getWorld().getBlockAt(x, y, z)); } } } } private void checkForPlayers(Location start, Location end, Player caster) { double locX = caster.getLocation().getX(); double locY = caster.getLocation().getY(); double locZ = caster.getLocation().getZ(); List<Entity> entities = caster.getNearbyEntities(locX, locY, locZ); int amount = 0; for(Entity e : entities) { if (start.distanceSquared(end) > start.distanceSquared(e.getLocation())) { e.remove(); amount++; } } caster.sendMessage(ChatColor.RED + "You killed " + amount + " entities"); }}
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
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()); } }
@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?
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) }
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));
@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
@bergerkiller I know what % means 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 If you know any better way (more efficient) or more accurate way of doing this then please tell me.
@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.
@bergerkillerOk well this is what I have so far if your wondering : Code:java Code:java public class Earthquake { private int damage; private int density; private int skillLevel; private float mana; private float radius; private float coolDown; public Earthquake(int damage, float radius, float mana, Player p, int skillLevel, float coolDown, int density) { this.damage = damage; this.skillLevel = skillLevel; this.radius = radius; this.mana = mana; this.coolDown = coolDown; this.density = density; this.initiateEarthquake(p); } public void initiateEarthquake(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); } private double getDamageFactor(double dist) { return (double)dist / damage; } private void destroyBlocks(Block b) { World w = b.getWorld(); Random r = new Random(); if (r.nextInt(6) == 1 ) { w.dropItemNaturally(b.getLocation(), new ItemStack(1)); b.setType(Material.AIR); } w.playEffect(b.getLocation(), Effect.SMOKE, density); } private void HeroToThere(Location start, Location end, Player caster) { double locX = caster.getLocation().getX(); double locY = caster.getLocation().getY(); double locZ = caster.getLocation().getZ(); HashSet<Block> blocks = new HashSet<Block>(); Random rand = new Random(); for(int x=(int)locX ; x<this.radius ; x++) { for(int y=(int)locY ; y<this.radius ; y++) { for(int z=(int)locZ ; z<this.radius ; z++) { blocks.add(caster.getWorld().getBlockAt(x, y, z)); } } } } private void checkForPlayers(Location start, Location end, Player caster) { double locX = caster.getLocation().getX(); double locY = caster.getLocation().getY(); double locZ = caster.getLocation().getZ(); List<LivingEntity> le = caster.getWorld().getLivingEntities(); for(LivingEntity e : le) { if (start.distance(end) > start.distance(e.getLocation())) { e.setHealth((int)(e.getHealth() - this.getDamageFactor(start.distance(e.getLocation())))); } } }}[LEFT][/LEFT]
@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?
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
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.
@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.
@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?
@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)
Ok 2 things 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
@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.
Thank you very much Also I edited it a little since I saw you made it in your TrainCarts plugin Also how do I run it? Do I called the "makeEarthquake" class?
http://s708.photobucket.com/albums/ww88/bergerkiller/?action=view¤t=quake.mp4 Gives you an idea of what it does. And yes, you call the makeEarthquake function. In the vid I used the player location => block as epicentre.
@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"));