package com.nisovin.magicspells.spells.targeted; import java.util.ArrayList; import java.util.EnumSet; import java.util.Iterator; import java.util.List; import java.util.Set; import org.bukkit.Bukkit; import org.bukkit.Location; import org.bukkit.Material; import org.bukkit.block.Block; import org.bukkit.entity.FallingBlock; import org.bukkit.entity.LivingEntity; import org.bukkit.entity.Player; import org.bukkit.event.EventHandler; import org.bukkit.event.Listener; import org.bukkit.event.entity.EntityChangeBlockEvent; import org.bukkit.util.Vector; import com.nisovin.magicspells.MagicSpells; import com.nisovin.magicspells.events.SpellTargetLocationEvent; import com.nisovin.magicspells.materials.MagicBlockMaterial; import com.nisovin.magicspells.materials.MagicMaterial; import com.nisovin.magicspells.spells.TargetedEntityFromLocationSpell; import com.nisovin.magicspells.spells.TargetedLocationSpell; import com.nisovin.magicspells.spells.TargetedSpell; import com.nisovin.magicspells.util.MagicConfig; public class DestroySpell extends TargetedSpell implements TargetedLocationSpell, TargetedEntityFromLocationSpell { int horizRadius; int vertRadius; int horizRadiusSq; int vertRadiusSq; float velocity; VelocityType velocityType; boolean preventLandingBlocks; int fallingBlockDamage; Set<Material> blockTypesToThrow; Set<Material> blockTypesToRemove; Set<FallingBlock> fallingBlocks; public DestroySpell(MagicConfig config, String spellName) { super(config, spellName); horizRadius = getConfigInt("horiz-radius", 3); horizRadiusSq = horizRadius * horizRadius; vertRadius = getConfigInt("vert-radius", 3); vertRadiusSq = vertRadius * vertRadius; velocity = getConfigFloat("velocity", 0); String vType = getConfigString("velocity-type", "none"); if (vType.equalsIgnoreCase("out")) { velocityType = VelocityType.OUT; } else if (vType.equalsIgnoreCase("up")) { velocityType = VelocityType.UP; } else if (vType.equalsIgnoreCase("fountain")) { velocityType = VelocityType.UP_OUT; } else if (vType.equalsIgnoreCase("random")) { velocityType = VelocityType.RANDOM; } else if (vType.equalsIgnoreCase("randomup")) { velocityType = VelocityType.RANDOMUP; } else if (vType.equalsIgnoreCase("down")) { velocityType = VelocityType.DOWN; } else if (vType.equalsIgnoreCase("toward")) { velocityType = VelocityType.TOWARD; } else if (vType.equalsIgnoreCase("away")) { velocityType = VelocityType.AWAY; } else { velocityType = VelocityType.NONE; } preventLandingBlocks = config.getBoolean("prevent-landing-blocks", false); fallingBlockDamage = getConfigInt("falling-block-damage", 0); List<String> toThrow = getConfigStringList("block-types-to-throw", null); if (toThrow != null && toThrow.size() > 0) { blockTypesToThrow = EnumSet.noneOf(Material.class); for (String s : toThrow) { MagicMaterial m = MagicSpells.getItemNameResolver().resolveBlock(s); if (m != null && m.getMaterial() != null) { blockTypesToThrow.add(m.getMaterial()); } } } List<String> toRemove = getConfigStringList("block-types-to-remove", null); if (toRemove != null && toRemove.size() > 0) { blockTypesToRemove = EnumSet.noneOf(Material.class); for (String s : toRemove) { MagicMaterial m = MagicSpells.getItemNameResolver().resolveBlock(s); if (m != null && m.getMaterial() != null) { blockTypesToRemove.add(m.getMaterial()); } } } if (preventLandingBlocks) { registerEvents(new FallingBlockListener()); MagicSpells.scheduleRepeatingTask(new Runnable() { public void run() { if (fallingBlocks.size() > 0) { Iterator<FallingBlock> iter = fallingBlocks.iterator(); while (iter.hasNext()) { if (!iter.next().isValid()) { iter.remove(); } } } } }, 600, 600); } } @Override public PostCastAction castSpell(Player player, SpellCastState state, float power, String[] args) { if (state == SpellCastState.NORMAL) { Block b = getTargetedBlock(player, power); if (b != null && b.getType() != Material.AIR) { SpellTargetLocationEvent event = new SpellTargetLocationEvent(this, player, b.getLocation(), power); Bukkit.getPluginManager().callEvent(event); if (event.isCancelled()) { b = null; } else { b = event.getTargetLocation().getBlock(); power = event.getPower(); } } if (b != null && b.getType() != Material.AIR) { Location loc = b.getLocation().add(.5, .5, .5); doIt(player.getLocation(), loc); playSpellEffects(player, loc); } } return PostCastAction.HANDLE_NORMALLY; } private void doIt(Location source, Location target) { int centerX = target.getBlockX(); int centerY = target.getBlockY(); int centerZ = target.getBlockZ(); List<Block> blocksToThrow = new ArrayList<Block>(); List<Block> blocksToRemove = new ArrayList<Block>(); for (int y = centerY - vertRadius; y <= centerY + vertRadius; y++) { for (int x = centerX - horizRadius; x <= centerX + horizRadius; x++) { for (int z = centerZ - horizRadius; z <= centerZ + horizRadius; z++) { Block b = target.getWorld().getBlockAt(x, y, z); if (b.getType() != Material.BEDROCK && b.getType() != Material.AIR) { if (blockTypesToThrow != null) { if (blockTypesToThrow.contains(b.getType())) { blocksToThrow.add(b); } else if (blockTypesToRemove != null) { if (blockTypesToRemove.contains(b.getType())) { blocksToRemove.add(b); } } else if (!b.getType().isSolid()) { blocksToRemove.add(b); } } else { if (b.getType().isSolid()) { blocksToThrow.add(b); } else { blocksToRemove.add(b); } } } } } } for (Block b : blocksToRemove) { b.setType(Material.AIR); } for (Block b : blocksToThrow) { MagicMaterial mat = new MagicBlockMaterial(b.getState().getData()); Location l = new Location(target.getWorld(), b.getX() + 0.5, b.getY() + 0.5, b.getZ() + 0.5); FallingBlock fb = mat.spawnFallingBlock(l); fb.setDropItem(false); Vector v = null; if (velocityType == VelocityType.OUT) { v = l.toVector().subtract(target.toVector()).normalize(); v.setX(v.getX() + ((Math.random() - .5) / 4)); v.setZ(v.getZ() + ((Math.random() - .5) / 4)); v.multiply(velocity); } else if (velocityType == VelocityType.UP) { v = new Vector(0, velocity, 0); v.setY(v.getY() + ((Math.random() - .5) / 4)); } else if (velocityType == VelocityType.UP_OUT) { v = l.toVector().setY(0).subtract(target.toVector().setY(0)).normalize().setY(1); v.setX(v.getX() + ((Math.random() - .5) / 4)); v.setZ(v.getZ() + ((Math.random() - .5) / 4)); v.multiply(velocity); } else if (velocityType == VelocityType.RANDOM) { v = new Vector(Math.random() - .5, Math.random() - .5, Math.random() - .5); v.normalize().multiply(velocity); } else if (velocityType == VelocityType.RANDOMUP) { v = new Vector(Math.random() - .5, Math.random() / 2, Math.random() - .5); v.normalize().multiply(velocity); fb.setVelocity(v); } else if (velocityType == VelocityType.DOWN) { v = new Vector(0, -velocity, 0); } else if (velocityType == VelocityType.TOWARD) { v = source.toVector().subtract(l.toVector()).normalize().multiply(velocity); } else if (velocityType == VelocityType.AWAY) { v = l.toVector().subtract(source.toVector()).normalize().multiply(velocity); } else { v = new Vector(0, (Math.random() - .5) / 4, 0); } if (v != null) { fb.setVelocity(v); } if (fallingBlockDamage > 0) { MagicSpells.getVolatileCodeHandler().setFallingBlockHurtEntities(fb, fallingBlockDamage, fallingBlockDamage); } if (preventLandingBlocks) { fallingBlocks.add(fb); } b.setType(Material.AIR); } } /*private boolean blockInRange(Block block, Location location) { Location l = block.getLocation().add(.5, .5, .5); System.out.println(distanceSq(l.getX(), l.getZ(), location.getX(), location.getZ()) + " - " + horizRadiusSq); System.out.println(distanceSq(l.getY(), l.getZ(), location.getY(), location.getZ()) + " - " + vertRadiusSq); return distanceSq(l.getX(), l.getZ(), location.getX(), location.getZ()) < horizRadiusSq && distanceSq(l.getY(), l.getZ(), location.getY(), location.getZ()) < vertRadiusSq && distanceSq(l.getY(), l.getX(), location.getY(), location.getX()) < vertRadiusSq; } private double distanceSq(double x1, double y1, double x2, double y2) { double xdiff = (x2 - x1); double ydiff = (y2 - y1); return xdiff * xdiff + ydiff * ydiff; }*/ @Override public boolean castAtLocation(Player caster, Location target, float power) { doIt(caster.getLocation(), target); playSpellEffects(caster, target); return true; } @Override public boolean castAtLocation(Location target, float power) { return false; } @Override public boolean castAtEntityFromLocation(Player caster, Location from, LivingEntity target, float power) { doIt(from, target.getLocation()); playSpellEffects(from, target); return true; } @Override public boolean castAtEntityFromLocation(Location from, LivingEntity target, float power) { doIt(from, target.getLocation()); playSpellEffects(from, target); return true; } class FallingBlockListener implements Listener { @EventHandler public void onBlockLand(EntityChangeBlockEvent event) { boolean removed = fallingBlocks.remove(event.getEntity()); if (removed) { event.setCancelled(true); } } } public enum VelocityType { NONE, UP, OUT, UP_OUT, RANDOM, RANDOMUP, DOWN, TOWARD, AWAY } }