package com.nisovin.magicspells.spells.instant; import java.util.List; import java.util.TreeSet; import org.bukkit.Bukkit; import org.bukkit.Location; import org.bukkit.World; import org.bukkit.block.Block; import org.bukkit.entity.Entity; import org.bukkit.entity.EntityType; import org.bukkit.entity.LivingEntity; import org.bukkit.entity.Player; import org.bukkit.util.Vector; import com.nisovin.magicspells.MagicSpells; import com.nisovin.magicspells.events.SpellTargetEvent; import com.nisovin.magicspells.materials.MagicMaterial; import com.nisovin.magicspells.spelleffects.EffectPosition; import com.nisovin.magicspells.spells.InstantSpell; import com.nisovin.magicspells.util.MagicConfig; import com.nisovin.magicspells.util.Util; public class DowseSpell extends InstantSpell { private MagicMaterial material; private EntityType entityType; private String playerName; private int radius; private boolean rotatePlayer; private boolean setCompass; private String strNotFound; private boolean getDistance; public DowseSpell(MagicConfig config, String spellName) { super(config, spellName); String blockName = getConfigString("block-type", ""); if (!blockName.isEmpty()) { material = MagicSpells.getItemNameResolver().resolveBlock(blockName); } String entityName = getConfigString("entity-type", ""); if (!entityName.isEmpty()) { if (entityName.equalsIgnoreCase("player")) { entityType = EntityType.PLAYER; } else if (entityName.toLowerCase().startsWith("player:")) { entityType = EntityType.PLAYER; playerName = entityName.split(":")[1]; } else { entityType = Util.getEntityType(entityName); } } radius = getConfigInt("radius", 4); rotatePlayer = getConfigBoolean("rotate-player", true); setCompass = getConfigBoolean("set-compass", true); strNotFound = getConfigString("str-not-found", "No dowsing target found."); getDistance = strCastSelf != null && strCastSelf.contains("%d"); if (material == null && entityType == null) { MagicSpells.error("DowseSpell '" + internalName + "' has no dowse target (block or entity) defined"); } } @Override public PostCastAction castSpell(Player player, SpellCastState state, float power, String[] args) { if (state == SpellCastState.NORMAL) { int distance = -1; if (material != null) { Block foundBlock = null; Location loc = player.getLocation(); World world = player.getWorld(); int cx = loc.getBlockX(); int cy = loc.getBlockY(); int cz = loc.getBlockZ(); for (int r = 1; r <= Math.round(radius * power); r++) { for (int x = -r; x <= r; x++) { for (int y = -r; y <= r; y++) { for (int z = -r; z <= r; z++) { if (x == r || y == r || z == r || -x == r || -y == r || -z == r) { Block block = world.getBlockAt(cx + x, cy + y, cz + z); if (material.equals(block)) { foundBlock = block; break; } } } if (foundBlock != null) break; } if (foundBlock != null) break; } if (foundBlock != null) break; } if (foundBlock == null) { sendMessage(player, strNotFound); return PostCastAction.ALREADY_HANDLED; } else { if (rotatePlayer) { Vector v = foundBlock.getLocation().add(.5, .5, .5).subtract(player.getEyeLocation()).toVector().normalize(); Util.setFacing(player, v); } if (setCompass) { player.setCompassTarget(foundBlock.getLocation()); } if (getDistance) { distance = (int)Math.round(player.getLocation().distance(foundBlock.getLocation())); } } } else if (entityType != null) { // find entity Entity foundEntity = null; double distanceSq = radius * radius; if (entityType == EntityType.PLAYER && playerName != null) { // find specific player foundEntity = Bukkit.getPlayerExact(playerName); if (foundEntity != null) { if (!foundEntity.getWorld().equals(player.getWorld())) { foundEntity = null; } else if (radius > 0 && player.getLocation().distanceSquared(foundEntity.getLocation()) > distanceSq) { foundEntity = null; } } } else { // find nearest entity List<Entity> nearby = player.getNearbyEntities(radius, radius, radius); Location playerLoc = player.getLocation(); TreeSet<NearbyEntity> ordered = new TreeSet<NearbyEntity>(); for (Entity e : nearby) { if (e.getType() == entityType) { double d = e.getLocation().distanceSquared(playerLoc); if (d < distanceSq) { ordered.add(new NearbyEntity(e, d)); } } } if (ordered.size() > 0) { for (NearbyEntity ne : ordered) { if (ne.entity instanceof LivingEntity) { SpellTargetEvent event = new SpellTargetEvent(this, player, (LivingEntity)ne.entity, power); Bukkit.getPluginManager().callEvent(event); if (!event.isCancelled()) { foundEntity = ne.entity; break; } } else { foundEntity = ne.entity; break; } } } } if (foundEntity == null) { sendMessage(player, strNotFound); return PostCastAction.ALREADY_HANDLED; } else { if (rotatePlayer) { Location l = (foundEntity instanceof LivingEntity ? ((LivingEntity)foundEntity).getEyeLocation() : foundEntity.getLocation()); Vector v = l.subtract(player.getEyeLocation()).toVector().normalize(); Util.setFacing(player, v); } if (setCompass) { player.setCompassTarget(foundEntity.getLocation()); } if (getDistance) { distance = (int)Math.round(player.getLocation().distance(foundEntity.getLocation())); } } } playSpellEffects(EffectPosition.CASTER, player); if (getDistance) { sendMessage(player, strCastSelf, "%d", distance+""); sendMessageNear(player, strCastOthers); return PostCastAction.NO_MESSAGES; } } return PostCastAction.HANDLE_NORMALLY; } class NearbyEntity implements Comparable<NearbyEntity> { Entity entity; double distanceSquared; public NearbyEntity(Entity entity, double distanceSquared) { this.entity = entity; this.distanceSquared = distanceSquared; } @Override public int compareTo(NearbyEntity e) { if (e.distanceSquared < this.distanceSquared) { return -1; } else if (e.distanceSquared > this.distanceSquared) { return 1; } else { return 0; } } } }