package com.nisovin.magicspells.spells.targeted; import java.util.ArrayList; import java.util.HashMap; import org.bukkit.Bukkit; import org.bukkit.entity.Entity; import org.bukkit.entity.LivingEntity; import org.bukkit.entity.Player; import org.bukkit.event.EventHandler; import org.bukkit.event.EventPriority; import org.bukkit.event.Listener; import org.bukkit.event.entity.EntityDamageByEntityEvent; import org.bukkit.event.entity.PlayerDeathEvent; import org.bukkit.event.player.PlayerItemHeldEvent; import org.bukkit.util.Vector; import com.nisovin.magicspells.MagicSpells; import com.nisovin.magicspells.events.SpellCastEvent; import com.nisovin.magicspells.spells.TargetedEntitySpell; import com.nisovin.magicspells.spells.TargetedSpell; import com.nisovin.magicspells.util.MagicConfig; import com.nisovin.magicspells.util.TargetInfo; public class LevitateSpell extends TargetedSpell implements TargetedEntitySpell { private int tickRate; private int duration; private float distanceChange; private float minDistance; private boolean cancelOnItemSwitch; private boolean cancelOnSpellCast; private boolean cancelOnTakeDamage; private HashMap<Player,Levitator> levitating; public LevitateSpell(MagicConfig config, String spellName) { super(config, spellName); tickRate = getConfigInt("tick-rate", 5); duration = getConfigInt("duration", 10); distanceChange = getConfigFloat("distance-change", 0); minDistance = getConfigFloat("min-distance", 1); cancelOnItemSwitch = getConfigBoolean("cancel-on-item-switch", true); cancelOnSpellCast = getConfigBoolean("cancel-on-spell-cast", false); cancelOnTakeDamage = getConfigBoolean("cancel-on-take-damage", true); levitating = new HashMap<Player,Levitator>(); } @Override public void initialize() { super.initialize(); if (cancelOnItemSwitch) { registerEvents(new ItemSwitchListener()); } if (cancelOnSpellCast) { registerEvents(new SpellCastListener()); } if (cancelOnTakeDamage) { registerEvents(new DamageListener()); } } @Override public PostCastAction castSpell(Player player, SpellCastState state, float power, String[] args) { if (levitating.containsKey(player)) { levitating.remove(player).stop(); return PostCastAction.ALREADY_HANDLED; } else if (state == SpellCastState.NORMAL) { TargetInfo<LivingEntity> target = getTargetedEntity(player, power); if (target == null) { return noTarget(player); } levitate(player, target.getTarget(), target.getPower()); sendMessages(player, target.getTarget()); return PostCastAction.NO_MESSAGES; } return PostCastAction.HANDLE_NORMALLY; } private void levitate(Player player, Entity target, float power) { double distance = player.getLocation().distance(target.getLocation()); int duration = this.duration > 0 ? Math.round(this.duration * (20F/tickRate) * power) : 0; Levitator lev = new Levitator(player, target, duration, distance); levitating.put(player, lev); playSpellEffects(player, target); } @Override public boolean castAtEntity(Player caster, LivingEntity target, float power) { levitate(caster, target, power); return true; } @Override public boolean castAtEntity(LivingEntity target, float power) { return false; } public void onPlayerDeath(PlayerDeathEvent event) { if (levitating.containsKey(event.getEntity())) { levitating.remove(event.getEntity()).stop(); } } public class ItemSwitchListener implements Listener { @EventHandler public void onItemSwitch(PlayerItemHeldEvent event) { if (levitating.containsKey(event.getPlayer())) { levitating.remove(event.getPlayer()).stop(); } } } public class SpellCastListener implements Listener { @EventHandler(priority=EventPriority.MONITOR, ignoreCancelled=true) public void onSpellCast(SpellCastEvent event) { if (levitating.containsKey(event.getCaster()) && !event.getSpell().getInternalName().equals(internalName)) { levitating.remove(event.getCaster()).stop(); } } } public class DamageListener implements Listener { public void onEntityDamage(EntityDamageByEntityEvent event) { if (event.getEntity() instanceof Player && levitating.containsKey(event.getEntity())) { levitating.remove(event.getEntity()).stop(); } } } @Override public void turnOff() { for (Levitator l : new ArrayList<Levitator>(levitating.values())) { l.stop(); } levitating.clear(); } private class Levitator implements Runnable { private Player caster; private Entity target; private int duration; private double distance; private int counter; private int taskId; private boolean stopped; public Levitator(Player caster, Entity target, int duration, double distance) { this.caster = caster; this.target = target; this.duration = duration; this.distance = distance; this.counter = 0; taskId = Bukkit.getScheduler().scheduleSyncRepeatingTask(MagicSpells.plugin, this, tickRate, tickRate); stopped = false; } @Override public void run() { if (!stopped) { if (caster.isDead() || !caster.isOnline()) { stop(); } else { if (distanceChange != 0 && distance > minDistance) { distance -= distanceChange; if (distance < minDistance) distance = minDistance; } target.setFallDistance(0); Vector casterLocation = caster.getEyeLocation().toVector(); Vector targetLocation = target.getLocation().toVector(); Vector wantedLocation = casterLocation.add(caster.getLocation().getDirection().multiply(distance)); Vector v = wantedLocation.subtract(targetLocation).multiply(tickRate/25F + .1); target.setVelocity(v); counter++; if (duration > 0 && counter > duration) { stop(); } } } } public void stop() { Bukkit.getScheduler().cancelTask(taskId); stopped = true; levitating.remove(caster); } } }