package com.nisovin.magicspells.spells.instant; import java.util.HashMap; import java.util.Iterator; import java.util.Map; import org.bukkit.Bukkit; import org.bukkit.Location; import org.bukkit.entity.Player; import org.bukkit.event.EventHandler; import org.bukkit.event.EventPriority; import org.bukkit.event.entity.PlayerDeathEvent; import org.bukkit.event.player.PlayerInteractEntityEvent; import org.bukkit.event.player.PlayerQuitEvent; import com.nisovin.magicspells.MagicSpells; import com.nisovin.magicspells.Spell; import com.nisovin.magicspells.spelleffects.EffectPosition; import com.nisovin.magicspells.spells.InstantSpell; import com.nisovin.magicspells.util.MagicConfig; public class RitualSpell extends InstantSpell { private int ritualDuration; private int reqParticipants; private boolean needSpellToParticipate; private boolean showProgressOnExpBar; private boolean chargeReagentsImmediately; private boolean setCooldownImmediately; private boolean setCooldownForAll; private Spell spell; private String theSpellName; private int tickInterval; private int effectInterval; private String strRitualJoined; private String strRitualSuccess; private String strRitualInterrupted; private String strRitualFailed; private HashMap<Player, ActiveRitual> activeRituals; public RitualSpell(MagicConfig config, String spellName) { super(config, spellName); ritualDuration = getConfigInt("ritual-duration", 200); reqParticipants = getConfigInt("req-participants", 3); needSpellToParticipate = getConfigBoolean("need-spell-to-participate", false); showProgressOnExpBar = getConfigBoolean("show-progress-on-exp-bar", true); chargeReagentsImmediately = getConfigBoolean("charge-reagents-immediately", true); setCooldownImmediately = getConfigBoolean("set-cooldown-immediately", true); setCooldownForAll = getConfigBoolean("set-cooldown-for-all", true); theSpellName = getConfigString("spell", ""); tickInterval = getConfigInt("tick-interval", 5); effectInterval = getConfigInt("effect-interval", 20); strRitualJoined = getConfigString("str-ritual-joined", null); strRitualSuccess = getConfigString("str-ritual-success", null); strRitualInterrupted = getConfigString("str-ritual-interrupted", null); strRitualFailed = getConfigString("str-ritual-failed", null); activeRituals = new HashMap<Player, ActiveRitual>(); } @Override public void initialize() { super.initialize(); spell = MagicSpells.getSpellByInternalName(theSpellName); if (spell == null) { MagicSpells.error("RitualSpell '" + internalName + "' does not have a spell defined (" + theSpellName + ")!"); } } @Override public PostCastAction castSpell(Player player, SpellCastState state, float power, String[] args) { if (activeRituals.containsKey(player)) { ActiveRitual channel = activeRituals.remove(player); channel.stop(strRitualInterrupted); } if (state == SpellCastState.NORMAL) { activeRituals.put(player, new ActiveRitual(player, power, args)); if (!chargeReagentsImmediately && !setCooldownImmediately) { return PostCastAction.MESSAGES_ONLY; } else if (!chargeReagentsImmediately) { return PostCastAction.NO_REAGENTS; } else if (!setCooldownImmediately) { return PostCastAction.NO_COOLDOWN; } } return PostCastAction.HANDLE_NORMALLY; } @EventHandler(priority=EventPriority.MONITOR, ignoreCancelled=true) public void onPlayerInteract(PlayerInteractEntityEvent event) { if (event.getRightClicked() instanceof Player) { ActiveRitual channel = activeRituals.get((Player)event.getRightClicked()); if (channel != null) { if (!needSpellToParticipate || hasThisSpell(event.getPlayer())) { channel.addChanneler(event.getPlayer()); sendMessage(event.getPlayer(), strRitualJoined); } } } } @EventHandler public void onPlayerQuit(PlayerQuitEvent event) { for (ActiveRitual ritual : activeRituals.values()) { if (ritual.isChanneler(event.getPlayer())) { ritual.stop(strInterrupted); } } } @EventHandler(priority=EventPriority.MONITOR, ignoreCancelled=true) public void onPlayerDeath(PlayerDeathEvent event) { for (ActiveRitual ritual : activeRituals.values()) { if (ritual.isChanneler(event.getEntity())) { ritual.stop(strInterrupted); } } } private boolean hasThisSpell(Player player) { return MagicSpells.getSpellbook(player).hasSpell(this); } public class ActiveRitual implements Runnable { private Player caster; private float power; private String[] args; private int duration = 0; private int taskId; private HashMap<Player, Location> channelers = new HashMap<Player, Location>(); public ActiveRitual(Player caster, float power, String[] args) { this.power = power; this.args = args; this.caster = caster; this.channelers.put(caster, caster.getLocation().clone()); this.taskId = Bukkit.getScheduler().scheduleSyncRepeatingTask(MagicSpells.plugin, this, tickInterval, tickInterval); if (showProgressOnExpBar) { MagicSpells.getExpBarManager().lock(caster, this); } playSpellEffects(EffectPosition.CASTER, caster); } public void addChanneler(Player player) { if (!channelers.containsKey(player)) { channelers.put(player, player.getLocation().clone()); if (showProgressOnExpBar) { MagicSpells.getExpBarManager().lock(player, this); } playSpellEffects(EffectPosition.CASTER, player); } } public void removeChanneler(Player player) { channelers.remove(player); } public boolean isChanneler(Player player) { return channelers.containsKey(player); } @Override public void run() { duration += tickInterval; int count = channelers.size(); boolean interrupted = false; Iterator<Map.Entry<Player, Location>> iter = channelers.entrySet().iterator(); while (iter.hasNext()) { Player player = iter.next().getKey(); // check for movement/death/offline Location oldloc = channelers.get(player); Location newloc = player.getLocation(); if (!player.isOnline() || player.isDead() || Math.abs(oldloc.getX() - newloc.getX()) > .2 || Math.abs(oldloc.getY() - newloc.getY()) > .2 || Math.abs(oldloc.getZ() - newloc.getZ()) > .2) { if (player.getName().equals(caster.getName())) { interrupted = true; break; } else { iter.remove(); count--; resetManaBar(player); continue; } } // send exp bar update if (showProgressOnExpBar) { MagicSpells.getExpBarManager().update(player, count, (float)duration / (float)ritualDuration, this); } // spell effect if (duration % effectInterval == 0) { playSpellEffects(EffectPosition.CASTER, player); } } if (interrupted) { stop(strRitualInterrupted); if (spellOnInterrupt != null && caster.isValid()) { spellOnInterrupt.castSpell(caster, SpellCastState.NORMAL, power, null); } } if (duration >= ritualDuration) { // channel is done if (count >= reqParticipants && !caster.isDead() && caster.isOnline()) { if (chargeReagentsImmediately || hasReagents(caster)) { stop(strRitualSuccess); playSpellEffects(EffectPosition.DELAYED, caster); PostCastAction action = spell.castSpell(caster, SpellCastState.NORMAL, power, args); if (!chargeReagentsImmediately && action.chargeReagents()) { removeReagents(caster); } if (!setCooldownImmediately && action.setCooldown()) { setCooldown(caster, cooldown); } if (setCooldownForAll && action.setCooldown()) { for (Player p : channelers.keySet()) { setCooldown(p, cooldown); } } } else { stop(strRitualFailed); } } else { stop(strRitualFailed); } } } public void stop(String message) { for (Player player : channelers.keySet()) { sendMessage(player, message); resetManaBar(player); } channelers.clear(); Bukkit.getScheduler().cancelTask(taskId); activeRituals.remove(caster); } private void resetManaBar(Player player) { MagicSpells.getExpBarManager().unlock(player, this); MagicSpells.getExpBarManager().update(player, player.getLevel(), player.getExp()); if (MagicSpells.getManaHandler() != null) { MagicSpells.getManaHandler().showMana(player); } } } }