package com.nisovin.magicspells.spells; import java.util.HashMap; import java.util.List; import java.util.Map; import org.bukkit.Bukkit; import org.bukkit.ChatColor; import org.bukkit.Material; import org.bukkit.entity.EntityType; import org.bukkit.entity.LivingEntity; import org.bukkit.entity.Player; import org.bukkit.entity.Projectile; import org.bukkit.event.EventHandler; import org.bukkit.event.Listener; import org.bukkit.event.entity.EntityDamageByEntityEvent; import org.bukkit.event.entity.EntityShootBowEvent; import org.bukkit.event.entity.ProjectileHitEvent; import org.bukkit.inventory.ItemStack; import org.bukkit.metadata.FixedMetadataValue; import org.bukkit.metadata.MetadataValue; import com.nisovin.magicspells.MagicSpells; import com.nisovin.magicspells.Spell; import com.nisovin.magicspells.Spellbook; import com.nisovin.magicspells.Subspell; import com.nisovin.magicspells.events.SpellCastEvent; import com.nisovin.magicspells.events.SpellTargetEvent; import com.nisovin.magicspells.util.MagicConfig; import com.nisovin.magicspells.util.SpellReagents; public class ArrowSpell extends Spell { private static ArrowSpellHandler handler; String bowName; String spellNameOnHitEntity; String spellNameOnHitGround; Subspell spellOnHitEntity; Subspell spellOnHitGround; boolean useBowForce; public ArrowSpell(MagicConfig config, String spellName) { super(config, spellName); bowName = ChatColor.translateAlternateColorCodes('&', getConfigString("bow-name", null)); spellNameOnHitEntity = getConfigString("spell-on-hit-entity", null); spellNameOnHitGround = getConfigString("spell-on-hit-ground", null); useBowForce = getConfigBoolean("use-bow-force", true); } @Override public void initialize() { super.initialize(); if (spellNameOnHitEntity != null && !spellNameOnHitEntity.isEmpty()) { Subspell spell = new Subspell(spellNameOnHitEntity); if (spell.process() && spell.isTargetedEntitySpell()) { spellOnHitEntity = spell; } } if (spellNameOnHitGround != null && !spellNameOnHitGround.isEmpty()) { Subspell spell = new Subspell(spellNameOnHitGround); if (spell.process() && spell.isTargetedLocationSpell()) { spellOnHitGround = spell; } } if (handler == null) { handler = new ArrowSpellHandler(); } handler.registerSpell(this); } @Override public void turnOff() { super.turnOff(); if (handler != null) { handler.turnOff(); handler = null; } } @Override public PostCastAction castSpell(Player player, SpellCastState state, float power, String[] args) { return PostCastAction.ALREADY_HANDLED; } @Override public boolean canCastWithItem() { return false; } @Override public boolean canCastByCommand() { return false; } class ArrowSpellHandler implements Listener { Map<String, ArrowSpell> spells = new HashMap<String, ArrowSpell>(); public ArrowSpellHandler() { registerEvents(this); } public void registerSpell(ArrowSpell spell) { spells.put(spell.bowName, spell); } @EventHandler public void onArrowLaunch(EntityShootBowEvent event) { if (event.getEntity().getType() != EntityType.PLAYER) return; Player shooter = (Player)event.getEntity(); ItemStack inHand = shooter.getItemInHand(); if (inHand == null || inHand.getType() != Material.BOW) return; String bowName = inHand.getItemMeta().getDisplayName(); if (bowName != null && !bowName.isEmpty()) { Spellbook spellbook = MagicSpells.getSpellbook(shooter); ArrowSpell spell = spells.get(bowName); if (spell != null && spellbook.hasSpell(spell) && spellbook.canCast(spell)) { SpellReagents reagents = spell.reagents.clone(); SpellCastEvent castEvent = new SpellCastEvent(spell, shooter, SpellCastState.NORMAL, useBowForce ? event.getForce() : 1.0F, null, cooldown, reagents, castTime); Bukkit.getPluginManager().callEvent(castEvent); if (!castEvent.isCancelled()) { event.getProjectile().setMetadata("MSArrowSpell", new FixedMetadataValue(MagicSpells.plugin, new ArrowSpellData(spell, castEvent.getPower(), castEvent.getReagents()))); } else { event.setCancelled(true); event.getProjectile().remove(); } } } } @EventHandler public void onArrowHit(ProjectileHitEvent event) { final Projectile arrow = event.getEntity(); if (arrow.getType() != EntityType.ARROW) return; List<MetadataValue> metas = arrow.getMetadata("MSArrowSpell"); if (metas == null || metas.size() == 0) return; for (MetadataValue meta : metas) { final ArrowSpellData data = (ArrowSpellData)meta.value(); if (data.spell.spellOnHitGround != null) { MagicSpells.scheduleDelayedTask(new Runnable() { public void run() { Player shooter = (Player)arrow.getShooter(); if (!data.casted && !data.spell.onCooldown(shooter) && data.spell.hasReagents(shooter, data.reagents)) { boolean success = data.spell.spellOnHitGround.castAtLocation(shooter, arrow.getLocation(), data.power); if (success) { data.spell.setCooldown(shooter, data.spell.cooldown); data.spell.removeReagents(shooter, data.reagents); } data.casted = true; arrow.removeMetadata("MSArrowSpell", MagicSpells.plugin); } } }, 0); } break; } arrow.remove(); } @EventHandler(ignoreCancelled=true) public void onArrowHitEntity(EntityDamageByEntityEvent event) { if (event.getDamager().getType() != EntityType.ARROW) return; if (!(event.getEntity() instanceof LivingEntity)) return; Projectile arrow = (Projectile)event.getDamager(); List<MetadataValue> metas = arrow.getMetadata("MSArrowSpell"); if (metas == null || metas.size() == 0) return; Player shooter = (Player)arrow.getShooter(); for (MetadataValue meta : metas) { ArrowSpellData data = (ArrowSpellData)meta.value(); if (!data.spell.onCooldown(shooter)) { if (data.spell.spellOnHitEntity != null) { SpellTargetEvent evt = new SpellTargetEvent(data.spell, shooter, (LivingEntity)event.getEntity(), data.power); Bukkit.getPluginManager().callEvent(evt); if (!evt.isCancelled()) { data.spell.spellOnHitEntity.castAtEntity(shooter, (LivingEntity)event.getEntity(), evt.getPower()); data.spell.setCooldown(shooter, data.spell.cooldown); } data.casted = true; } } break; } arrow.remove(); arrow.removeMetadata("MSArrowSpell", MagicSpells.plugin); } public void turnOff() { unregisterEvents(this); spells.clear(); } } class ArrowSpellData { ArrowSpell spell; boolean casted = false; float power = 1.0F; SpellReagents reagents; public ArrowSpellData(ArrowSpell spell, float power, SpellReagents reagents) { this.spell = spell; this.power = power; this.reagents = reagents; } } }