package com.nisovin.magicspells.spells;
import java.util.ArrayList;
import java.util.List;
import java.util.Random;
import org.bukkit.Bukkit;
import org.bukkit.Location;
import org.bukkit.Material;
import org.bukkit.block.Block;
import org.bukkit.entity.LivingEntity;
import org.bukkit.entity.Player;
import com.nisovin.magicspells.MagicSpells;
import com.nisovin.magicspells.Subspell;
import com.nisovin.magicspells.spelleffects.EffectPosition;
import com.nisovin.magicspells.util.MagicConfig;
import com.nisovin.magicspells.util.TargetInfo;
public final class TargetedMultiSpell extends TargetedSpell implements TargetedEntitySpell, TargetedLocationSpell {
private boolean checkIndividualCooldowns;
private boolean requireEntityTarget;
private boolean pointBlank;
private int yOffset;
private boolean castRandomSpellInstead;
private boolean stopOnFail;
private List<String> spellList;
private ArrayList<Action> actions;
private Random random = new Random();
public TargetedMultiSpell(MagicConfig config, String spellName) {
super(config, spellName);
checkIndividualCooldowns = getConfigBoolean("check-individual-cooldowns", false);
requireEntityTarget = getConfigBoolean("require-entity-target", false);
pointBlank = getConfigBoolean("point-blank", false);
yOffset = getConfigInt("y-offset", 0);
castRandomSpellInstead = getConfigBoolean("cast-random-spell-instead", false);
stopOnFail = getConfigBoolean("stop-on-fail", true);
actions = new ArrayList<Action>();
spellList = getConfigStringList("spells", null);
}
@Override
public void initialize() {
super.initialize();
if (spellList != null) {
for (String s : spellList) {
if (s.matches("DELAY [0-9]+")) {
int delay = Integer.parseInt(s.split(" ")[1]);
actions.add(new Action(delay));
} else {
Subspell spell = new Subspell(s);
if (spell.process()) {
actions.add(new Action(spell));
} else {
MagicSpells.error("No such spell '" + s + "' for multi-spell '" + internalName + "'");
}
}
}
}
spellList = null;
}
@Override
public PostCastAction castSpell(Player player, SpellCastState state, float power, String[] args) {
if (state == SpellCastState.NORMAL) {
// check cooldowns
if (checkIndividualCooldowns) {
for (Action action : actions) {
if (action.isSpell()) {
if (action.getSpell().getSpell().onCooldown(player)) {
// a spell is on cooldown
sendMessage(player, strOnCooldown);
return PostCastAction.ALREADY_HANDLED;
}
}
}
}
// get target
Location locTarget = null;
LivingEntity entTarget = null;
if (requireEntityTarget) {
TargetInfo<LivingEntity> info = getTargetedEntity(player, power);
if (info != null) {
entTarget = info.getTarget();
power = info.getPower();
}
} else if (pointBlank) {
locTarget = player.getLocation();
} else {
Block b = null;
try {
b = getTargetedBlock(player, power);
if (b != null && b.getType() != Material.AIR) {
locTarget = b.getLocation();
}
} catch (IllegalStateException e) {
b = null;
}
}
if (locTarget == null && entTarget == null) {
return noTarget(player);
}
if (locTarget != null) {
locTarget.setY(locTarget.getY() + yOffset);
}
boolean somethingWasDone = runSpells(player, entTarget, locTarget, power);
if (!somethingWasDone) {
return noTarget(player);
}
if (entTarget != null) {
sendMessages(player, entTarget);
return PostCastAction.NO_MESSAGES;
}
}
return PostCastAction.HANDLE_NORMALLY;
}
@Override
public boolean castAtLocation(Player caster, Location target, float power) {
return runSpells(caster, null, target, power);
}
@Override
public boolean castAtLocation(Location location, float power) {
return runSpells(null, null, location, power);
}
@Override
public boolean castAtEntity(Player caster, LivingEntity target, float power) {
return runSpells(caster, target, null, power);
}
@Override
public boolean castAtEntity(LivingEntity target, float power) {
return runSpells(null, target, null, power);
}
boolean runSpells(Player player, LivingEntity entTarget, Location locTarget, float power) {
boolean somethingWasDone = false;
if (!castRandomSpellInstead) {
int delay = 0;
Subspell spell;
List<DelayedSpell> delayedSpells = new ArrayList<DelayedSpell>();
for (Action action : actions) {
if (action.isDelay()) {
delay += action.getDelay();
} else if (action.isSpell()) {
spell = action.getSpell();
if (delay == 0) {
boolean ok = castTargetedSpell(spell, player, entTarget, locTarget, power);
if (ok) {
somethingWasDone = true;
} else {
// spell failed - exit loop
if (stopOnFail) {
break;
} else {
continue;
}
}
} else {
DelayedSpell ds = new DelayedSpell(spell, player, entTarget, locTarget, power, delayedSpells);
delayedSpells.add(ds);
Bukkit.getScheduler().scheduleSyncDelayedTask(MagicSpells.plugin, ds, delay);
somethingWasDone = true;
}
}
}
} else {
Action action = actions.get(random.nextInt(actions.size()));
if (action.isSpell()) {
somethingWasDone = castTargetedSpell(action.getSpell(), player, entTarget, locTarget, power);
} else {
somethingWasDone = false;
}
}
if (somethingWasDone) {
if (player != null) {
if (entTarget != null) {
playSpellEffects(player, entTarget);
} else if (locTarget != null) {
playSpellEffects(player, locTarget);
}
} else {
if (entTarget != null) {
playSpellEffects(EffectPosition.TARGET, entTarget);
} else if (locTarget != null) {
playSpellEffects(EffectPosition.TARGET, locTarget);
}
}
}
return somethingWasDone;
}
private boolean castTargetedSpell(Subspell spell, Player caster, LivingEntity entTarget, Location locTarget, float power) {
boolean success = false;
if (spell.isTargetedEntitySpell() && entTarget != null) {
success = spell.castAtEntity(caster, entTarget, power);
} else if (spell.isTargetedLocationSpell()) {
if (entTarget != null) {
success = spell.castAtLocation(caster, entTarget.getLocation(), power);
} else if (locTarget != null) {
success = spell.castAtLocation(caster, locTarget, power);
}
} else {
success = spell.cast(caster, power) == PostCastAction.HANDLE_NORMALLY;
}
return success;
}
private class Action {
private Subspell spell;
private int delay;
public Action(Subspell spell) {
this.spell = spell;
this.delay = 0;
}
public Action(int delay) {
this.delay = delay;
this.spell = null;
}
public boolean isSpell() {
return spell != null;
}
public Subspell getSpell() {
return spell;
}
public boolean isDelay() {
return delay > 0;
}
public int getDelay() {
return delay;
}
}
private class DelayedSpell implements Runnable {
private Subspell spell;
private Player player;
private LivingEntity entTarget;
private Location locTarget;
private float power;
private List<DelayedSpell> delayedSpells;
private boolean cancelled;
public DelayedSpell(Subspell spell, Player player, LivingEntity entTarget, Location locTarget, float power, List<DelayedSpell> delayedSpells) {
this.spell = spell;
this.player = player;
this.entTarget = entTarget;
this.locTarget = locTarget;
this.power = power;
this.delayedSpells = delayedSpells;
this.cancelled = false;
}
public void cancel() {
cancelled = true;
delayedSpells = null;
}
public void cancelAll() {
for (DelayedSpell ds : delayedSpells) {
if (ds != this) {
ds.cancel();
}
}
delayedSpells.clear();
cancel();
}
@Override
public void run() {
if (!cancelled) {
if (player == null || player.isValid()) {
boolean ok = castTargetedSpell(spell, player, entTarget, locTarget, power);
delayedSpells.remove(this);
if (!ok && stopOnFail) {
cancelAll();
}
} else {
cancelAll();
}
}
delayedSpells = null;
}
}
}