package com.nisovin.magicspells.spells.targeted; import java.util.HashMap; import java.util.Map; import org.bukkit.Bukkit; import org.bukkit.entity.LivingEntity; import org.bukkit.entity.Player; import org.bukkit.event.EventHandler; import org.bukkit.event.entity.EntityDamageByEntityEvent; import org.bukkit.event.entity.PlayerDeathEvent; import org.bukkit.event.entity.EntityDamageEvent.DamageCause; import com.nisovin.magicspells.MagicSpells; import com.nisovin.magicspells.events.SpellApplyDamageEvent; import com.nisovin.magicspells.spelleffects.EffectPosition; import com.nisovin.magicspells.spells.SpellDamageSpell; 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 DotSpell extends TargetedSpell implements TargetedEntitySpell, SpellDamageSpell { int delay; int interval; int duration; float damage; boolean preventKnockback; String spellDamageType; Map<Integer, Dot> activeDots = new HashMap<Integer, Dot>(); public DotSpell(MagicConfig config, String spellName) { super(config, spellName); delay = getConfigInt("delay", 1); interval = getConfigInt("interval", 20); duration = getConfigInt("duration", 200); damage = getConfigFloat("damage", 2); preventKnockback = getConfigBoolean("prevent-knockback", false); spellDamageType = getConfigString("spell-damage-type", ""); } @Override public PostCastAction castSpell(Player player, SpellCastState state, float power, String[] args) { if (state == SpellCastState.NORMAL) { TargetInfo<LivingEntity> targetInfo = getTargetedEntity(player, power); if (targetInfo == null) { return noTarget(player); } applyDot(player, targetInfo.getTarget(), targetInfo.getPower()); } return PostCastAction.HANDLE_NORMALLY; } void applyDot(Player caster, LivingEntity target, float power) { Dot dot = activeDots.get(target.getEntityId()); if (dot != null) { dot.dur = 0; dot.power = power; } else { dot = new Dot(caster, target, power); activeDots.put(target.getEntityId(), dot); } if (caster != null) { playSpellEffects(caster, target); } else { playSpellEffects(EffectPosition.TARGET, target); } } @Override public boolean castAtEntity(Player caster, LivingEntity target, float power) { applyDot(caster, target, power); return true; } @Override public boolean castAtEntity(LivingEntity target, float power) { applyDot(null, target, power); return true; } @EventHandler void onDeath(PlayerDeathEvent event) { Dot dot = activeDots.get(event.getEntity().getEntityId()); if (dot != null) { dot.cancel(); } } class Dot implements Runnable { Player caster; LivingEntity target; float power; int taskId; int dur = 0; public Dot(Player caster, LivingEntity target, float power) { this.caster = caster; this.target = target; this.power = power; taskId = MagicSpells.scheduleRepeatingTask(this, delay, interval); } public void run() { dur += interval; if (dur > duration) { cancel(); return; } if (target.isDead() || !target.isValid()) { cancel(); return; } double dam = damage * power; SpellApplyDamageEvent event = new SpellApplyDamageEvent(DotSpell.this, caster, target, dam, DamageCause.MAGIC, spellDamageType); Bukkit.getPluginManager().callEvent(event); dam = event.getFinalDamage(); if (preventKnockback) { // bukkit doesn't call a damage event here, so we'll do it ourselves @SuppressWarnings("deprecation") EntityDamageByEntityEvent devent = new EntityDamageByEntityEvent(caster, target, DamageCause.ENTITY_ATTACK, damage); Bukkit.getPluginManager().callEvent(devent); if (!devent.isCancelled()) { target.damage(devent.getDamage()); } } else { target.damage(dam, caster); } target.setNoDamageTicks(0); playSpellEffects(EffectPosition.DELAYED, target); } public void cancel() { MagicSpells.cancelTask(taskId); activeDots.remove(target.getEntityId()); } } @Override public String getSpellDamageType() { return spellDamageType; } }