package com.nisovin.magicspells.spells.targeted;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import org.bukkit.Bukkit;
import org.bukkit.Location;
import org.bukkit.Material;
import org.bukkit.block.Block;
import org.bukkit.entity.Entity;
import org.bukkit.entity.LivingEntity;
import org.bukkit.entity.Player;
import org.bukkit.util.Vector;
import com.nisovin.magicspells.MagicSpells;
import com.nisovin.magicspells.Subspell;
import com.nisovin.magicspells.castmodifiers.ModifierSet;
import com.nisovin.magicspells.events.SpellTargetEvent;
import com.nisovin.magicspells.events.SpellTargetLocationEvent;
import com.nisovin.magicspells.spelleffects.EffectPosition;
import com.nisovin.magicspells.spells.TargetedLocationSpell;
import com.nisovin.magicspells.spells.TargetedSpell;
import com.nisovin.magicspells.util.BoundingBox;
import com.nisovin.magicspells.util.MagicConfig;
public class AreaEffectSpell extends TargetedSpell implements TargetedLocationSpell {
private int radius;
private int verticalRadius;
private boolean pointBlank;
private int cone;
private boolean failIfNoTargets;
private int maxTargets;
private List<String> spellNames;
private boolean spellSourceInCenter;
private List<Subspell> spells;
private ModifierSet locationTargetModifiers;
private ModifierSet entityTargetModifiers;
public AreaEffectSpell(MagicConfig config, String spellName) {
super(config, spellName);
radius = getConfigInt("horizontal-radius", 10);
verticalRadius = getConfigInt("vertical-radius", 5);
pointBlank = getConfigBoolean("point-blank", true);
cone = getConfigInt("cone", 0);
failIfNoTargets = getConfigBoolean("fail-if-no-targets", true);
maxTargets = getConfigInt("max-targets", 0);
spellSourceInCenter = getConfigBoolean("spell-source-in-center", false);
spellNames = getConfigStringList("spells", null);
List<String> list = getConfigStringList("location-target-modifiers", null);
if (list != null) {
locationTargetModifiers = new ModifierSet(list);
}
list = getConfigStringList("entity-target-modifiers", null);
if (list != null) {
entityTargetModifiers = new ModifierSet(list);
}
}
@Override
public void initialize() {
super.initialize();
spells = new ArrayList<Subspell>();
if (spellNames != null && spellNames.size() > 0) {
for (String spellName : spellNames) {
Subspell spell = new Subspell(spellName);
if (spell.process()) {
if (spell.isTargetedEntityFromLocationSpell() || spell.isTargetedEntitySpell() || spell.isTargetedLocationSpell()) {
spells.add(spell);
} else {
MagicSpells.error("AreaEffect spell '" + name + "' attempted to use non-targeted spell '" + spellName + "'");
}
} else {
MagicSpells.error("AreaEffect spell '" + name + "' attempted to use invalid spell '" + spellName + "'");
}
}
spellNames.clear();
spellNames = null;
}
if (spells.size() == 0) {
MagicSpells.error("AreaEffect spell '" + name + "' has no spells!");
}
}
@Override
public PostCastAction castSpell(Player player, SpellCastState state, float power, String[] args) {
if (state == SpellCastState.NORMAL) {
// get location for aoe
Location loc = null;
if (pointBlank) {
loc = player.getLocation();
} else {
try {
Block block = getTargetedBlock(player, power);
if (block != null && block.getType() != Material.AIR) {
loc = block.getLocation();
}
} catch (IllegalStateException e) {
loc = null;
}
}
if (loc != null) {
SpellTargetLocationEvent event = new SpellTargetLocationEvent(this, player, loc, power);
Bukkit.getPluginManager().callEvent(event);
if (locationTargetModifiers != null) {
locationTargetModifiers.apply(event);
}
if (event.isCancelled()) {
loc = null;
} else {
loc = event.getTargetLocation();
power = event.getPower();
}
}
if (loc == null) {
return noTarget(player);
}
// cast spells on nearby entities
boolean done = doAoe(player, loc, power);
// check if no targets
if (!done && failIfNoTargets) {
return noTarget(player);
}
}
return PostCastAction.HANDLE_NORMALLY;
}
private boolean doAoe(Player player, Location location, float basePower) {
int count = 0;
Vector facing = player != null ? player.getLocation().getDirection() : location.getDirection();
Vector vLoc = player != null ? player.getLocation().toVector() : location.toVector();
BoundingBox box = new BoundingBox(location, radius, verticalRadius);
List<Entity> entities = new ArrayList<Entity>(location.getWorld().getEntitiesByClasses(LivingEntity.class));
Collections.shuffle(entities);
for (Entity e : entities) {
if (e instanceof LivingEntity && box.contains(e)) {
if (pointBlank && cone > 0) {
Vector dir = e.getLocation().toVector().subtract(vLoc);
if (Math.abs(dir.angle(facing)) > cone) {
continue;
}
}
LivingEntity target = (LivingEntity)e;
float power = basePower;
if (!target.isDead() && ((player == null && validTargetList.canTarget(target)) || validTargetList.canTarget(player, target))) {
if (player != null) {
SpellTargetEvent event = new SpellTargetEvent(this, player, target, power);
Bukkit.getPluginManager().callEvent(event);
if (entityTargetModifiers != null) {
entityTargetModifiers.apply(event);
}
if (event.isCancelled()) {
continue;
} else {
target = event.getTarget();
power = event.getPower();
}
} else if (entityTargetModifiers != null) {
SpellTargetEvent event = new SpellTargetEvent(this, player, target, power);
entityTargetModifiers.apply(event);
if (event.isCancelled()) {
continue;
}
}
for (Subspell spell : spells) {
if (player != null) {
if (spellSourceInCenter && spell.isTargetedEntityFromLocationSpell()) {
spell.castAtEntityFromLocation(player, location, target, power);
} else if (spell.isTargetedEntitySpell()) {
spell.castAtEntity(player, target, power);
} else if (spell.isTargetedLocationSpell()) {
spell.castAtLocation(player, target.getLocation(), power);
}
} else {
if (spell.isTargetedEntityFromLocationSpell()) {
spell.castAtEntityFromLocation(null, location, target, power);
} else if (spell.isTargetedEntitySpell()) {
spell.castAtEntity(null, target, power);
} else if (spell.isTargetedLocationSpell()) {
spell.castAtLocation(null, target.getLocation(), power);
}
}
}
playSpellEffects(EffectPosition.TARGET, target);
if (spellSourceInCenter) {
playSpellEffectsTrail(location, target.getLocation());
} else if (player != null) {
playSpellEffectsTrail(player.getLocation(), target.getLocation());
}
count++;
if (maxTargets > 0 && count >= maxTargets) {
break;
}
}
}
}
if (count > 0 || !failIfNoTargets) {
if (player != null) {
playSpellEffects(EffectPosition.CASTER, player);
}
playSpellEffects(EffectPosition.SPECIAL, location);
}
return count > 0;
}
@Override
public boolean castAtLocation(Player caster, Location target, float power) {
return doAoe(caster, target, power);
}
@Override
public boolean castAtLocation(Location target, float power) {
return doAoe(null, target, power);
}
}