package com.nisovin.magicspells.spells.targeted; import java.util.ArrayList; import java.util.HashMap; import org.bukkit.Bukkit; import org.bukkit.Location; import org.bukkit.Material; import org.bukkit.block.Block; import org.bukkit.entity.Arrow; import org.bukkit.entity.LivingEntity; import org.bukkit.entity.Player; import org.bukkit.metadata.FixedMetadataValue; import org.bukkit.util.Vector; import com.nisovin.magicspells.MagicSpells; import com.nisovin.magicspells.spelleffects.EffectPosition; 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 VolleySpell extends TargetedSpell implements TargetedLocationSpell, TargetedEntityFromLocationSpell { private VolleySpell thisSpell; private int arrows; private int speed; private int spread; private int fire; private int shootInterval; private int removeDelay; private boolean noTarget; private boolean powerAffectsArrowCount; private boolean powerAffectsSpeed; public VolleySpell(MagicConfig config, String spellName) { super(config, spellName); thisSpell = this; arrows = getConfigInt("arrows", 10); speed = getConfigInt("speed", 20); spread = getConfigInt("spread", 150); fire = getConfigInt("fire", 0); shootInterval = getConfigInt("shoot-interval", 0); removeDelay = getConfigInt("remove-delay", 0); noTarget = getConfigBoolean("no-target", false); powerAffectsArrowCount = getConfigBoolean("power-affects-arrow-count", true); powerAffectsSpeed = getConfigBoolean("power-affects-speed", false); } @Override public PostCastAction castSpell(Player player, SpellCastState state, float power, String[] args) { if (state == SpellCastState.NORMAL) { if (noTarget) { volley(player, player.getLocation(), null, power); } else { Block target; try { target = getTargetedBlock(player, power); } catch (IllegalStateException e) { target = null; } if (target == null || target.getType() == Material.AIR) { return noTarget(player); } else { volley(player, player.getLocation(), target.getLocation(), power); } } } return PostCastAction.HANDLE_NORMALLY; } private void volley(Player player, Location from, Location target, float power) { Location spawn = from.clone(); spawn.setY(spawn.getY()+3); Vector v; if (noTarget || target == null) { v = from.getDirection(); } else { v = target.toVector().subtract(spawn.toVector()).normalize(); } if (shootInterval <= 0) { final ArrayList<Arrow> arrowList = new ArrayList<Arrow>(); int arrows = powerAffectsArrowCount ? Math.round(this.arrows*power) : this.arrows; for (int i = 0; i < arrows; i++) { float speed = this.speed / 10F; if (powerAffectsSpeed) speed *= power; Arrow a = from.getWorld().spawnArrow(spawn, v, speed, (spread/10.0F)); a.setVelocity(a.getVelocity()); if (player != null) { a.setShooter(player); } if (fire > 0) { a.setFireTicks(fire); } a.setMetadata("MagicSpellsSource", new FixedMetadataValue(MagicSpells.plugin, "VolleySpell" + internalName)); if (removeDelay > 0) arrowList.add(a); } if (removeDelay > 0) { Bukkit.getScheduler().scheduleSyncDelayedTask(MagicSpells.plugin, new Runnable() { public void run() { for (Arrow a : arrowList) { a.remove(); } arrowList.clear(); } }, removeDelay); } } else { new ArrowShooter(player, spawn, v, power); } if (player != null) { if (target != null) { playSpellEffects(player, target); } else { playSpellEffects(EffectPosition.CASTER, player); } } else { if (from != null) { playSpellEffects(EffectPosition.CASTER, from); } if (target != null) { playSpellEffects(EffectPosition.TARGET, target); } } } @Override public boolean castAtLocation(Player caster, Location target, float power) { if (!noTarget) { volley(caster, caster.getLocation(), target, power); return true; } else { return false; } } @Override public boolean castAtLocation(Location target, float power) { return false; } @Override public boolean castAtEntityFromLocation(Player caster, Location from, LivingEntity target, float power) { if (!noTarget) { volley(caster, from, target.getLocation(), power); return true; } else { return false; } } @Override public boolean castAtEntityFromLocation(Location from, LivingEntity target, float power) { if (!noTarget) { volley(null, from, target.getLocation(), power); return true; } else { return false; } } private class ArrowShooter implements Runnable { Player player; Location spawn; Vector dir; int arrows; float speed; int taskId; int count; HashMap<Integer, Arrow> arrowMap; ArrowShooter(Player player, Location spawn, Vector dir, float power) { this.player = player; this.spawn = spawn; this.dir = dir; this.arrows = powerAffectsArrowCount ? Math.round(thisSpell.arrows * power) : thisSpell.arrows; this.speed = thisSpell.speed / 10F; if (powerAffectsSpeed) this.speed *= power; this.count = 0; if (removeDelay > 0) { this.arrowMap = new HashMap<Integer, Arrow>(); } this.taskId = Bukkit.getScheduler().scheduleSyncRepeatingTask(MagicSpells.plugin, this, 0, shootInterval); } @Override public void run() { // fire an arrow if (count < arrows) { Arrow a = spawn.getWorld().spawnArrow(spawn, dir, speed, (spread/10.0F)); a.setVelocity(a.getVelocity()); if (player != null) { a.setShooter(player); } if (fire > 0) { a.setFireTicks(fire); } a.setMetadata("MagicSpellsSource", new FixedMetadataValue(MagicSpells.plugin, "VolleySpell" + internalName)); if (removeDelay > 0) { arrowMap.put(count, a); } } // remove old arrow if (removeDelay > 0) { int old = count - removeDelay; if (old > 0) { Arrow a = arrowMap.remove(old); if (a != null) { a.remove(); } } } // end if it's done if (count >= arrows + removeDelay) { Bukkit.getScheduler().cancelTask(taskId); } count++; } } }