diff --git a/core/src/main/java/com/nisovin/magicspells/spells/buff/ProxySpell.java b/core/src/main/java/com/nisovin/magicspells/spells/buff/ProxySpell.java new file mode 100644 index 000000000..cc8499881 --- /dev/null +++ b/core/src/main/java/com/nisovin/magicspells/spells/buff/ProxySpell.java @@ -0,0 +1,108 @@ +package com.nisovin.magicspells.spells.buff; + +import java.util.Map; +import java.util.Set; +import java.util.UUID; +import java.util.HashMap; +import java.util.HashSet; +import java.util.Collection; + +import org.jetbrains.annotations.NotNull; + +import org.bukkit.event.EventHandler; +import org.bukkit.entity.LivingEntity; +import org.bukkit.event.EventPriority; +import org.bukkit.event.entity.EntityDamageByEntityEvent; + +import com.nisovin.magicspells.util.SpellData; +import com.nisovin.magicspells.spells.BuffSpell; +import com.nisovin.magicspells.util.MagicConfig; +import com.nisovin.magicspells.events.SpellTargetEvent; +import com.nisovin.magicspells.spelleffects.EffectPosition; + +public class ProxySpell extends BuffSpell { + + private final Set redirecting = new HashSet<>(); + private final Map proxies = new HashMap<>(); + + public ProxySpell(MagicConfig config, String spellName) { + super(config, spellName); + } + + @Override + public boolean castBuff(SpellData data) { + if (data.target().equals(data.caster())) return false; + proxies.put(data.target().getUniqueId(), data.caster()); + return true; + } + + @Override + public boolean recastBuff(SpellData data) { + stopEffects(data.target()); + turnOffBuff(data.target()); + return castBuff(data); + } + + @Override + public boolean isActive(LivingEntity entity) { + return proxies.containsKey(entity.getUniqueId()); + } + + @Override + public void turnOffBuff(LivingEntity entity) { + proxies.remove(entity.getUniqueId()); + } + + @Override + protected @NotNull Collection getActiveEntities() { + return proxies.keySet(); + } + + @EventHandler(ignoreCancelled = true) + public void onSpellTarget(SpellTargetEvent event) { + LivingEntity target = event.getTarget(); + if (target == null || !isActive(target)) return; + + LivingEntity proxyTarget = getProxyTarget(target); + if (proxyTarget == null) return; + + event.setTarget(proxyTarget); + playRedirectEffects(target, proxyTarget, event.getSpellData()); + + addUseAndChargeCost(target); + } + + @EventHandler(ignoreCancelled = true, priority = EventPriority.HIGHEST) + public void onEntityDamage(EntityDamageByEntityEvent event) { + if (!(event.getEntity() instanceof LivingEntity target) || !isActive(target)) return; + + LivingEntity proxyTarget = getProxyTarget(target); + if (proxyTarget == null) return; + if (!redirecting.add(proxyTarget.getUniqueId())) return; + + SpellData subData = new SpellData(event.getDamager() instanceof LivingEntity damager ? damager : null, proxyTarget); + playRedirectEffects(target, proxyTarget, subData); + + event.setCancelled(true); + try { + proxyTarget.damage(event.getFinalDamage(), event.getDamageSource()); + addUseAndChargeCost(target); + } finally { + redirecting.remove(proxyTarget.getUniqueId()); + } + } + + private LivingEntity getProxyTarget(LivingEntity target) { + LivingEntity proxyTarget = proxies.get(target.getUniqueId()); + if (proxyTarget != null && proxyTarget.isValid()) return proxyTarget; + + turnOff(target); + return null; + } + + private void playRedirectEffects(LivingEntity target, LivingEntity proxyTarget, SpellData data) { + playSpellEffects(EffectPosition.START_POSITION, target, data); + playSpellEffects(EffectPosition.END_POSITION, proxyTarget, data); + } + +}