package com.nisovin.magicspells.spells.targeted;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import org.bukkit.Bukkit;
import org.bukkit.Effect;
import org.bukkit.Location;
import org.bukkit.Material;
import org.bukkit.block.Block;
import org.bukkit.entity.Entity;
import org.bukkit.entity.Fireball;
import org.bukkit.entity.LivingEntity;
import org.bukkit.entity.Player;
import org.bukkit.entity.SmallFireball;
import org.bukkit.event.EventHandler;
import org.bukkit.event.EventPriority;
import org.bukkit.event.entity.EntityDamageByEntityEvent;
import org.bukkit.event.entity.EntityDamageEvent;
import org.bukkit.event.entity.ExplosionPrimeEvent;
import org.bukkit.event.entity.EntityDamageEvent.DamageCause;
import org.bukkit.util.Vector;
import com.nisovin.magicspells.MagicSpells;
import com.nisovin.magicspells.spelleffects.EffectPosition;
import com.nisovin.magicspells.spells.TargetedEntityFromLocationSpell;
import com.nisovin.magicspells.spells.TargetedSpell;
import com.nisovin.magicspells.util.BlockUtils;
import com.nisovin.magicspells.util.MagicConfig;
import com.nisovin.magicspells.util.TargetInfo;
import com.nisovin.magicspells.util.Util;
public class FireballSpell extends TargetedSpell implements TargetedEntityFromLocationSpell {
private boolean requireEntityTarget;
private boolean checkPlugins;
private float damageMultiplier;
private float explosionSize;
private boolean smallFireball;
private boolean noExplosion;
private boolean noExplosionEffect;
private int noExplosionDamage;
private int noExplosionDamageRange;
private boolean noFire;
private HashMap<Fireball,Float> fireballs;
private int taskId;
public FireballSpell(MagicConfig config, String spellName) {
super(config, spellName);
requireEntityTarget = getConfigBoolean("require-entity-target", false);
checkPlugins = getConfigBoolean("check-plugins", true);
damageMultiplier = getConfigFloat("damage-multiplier", 0);
explosionSize = getConfigFloat("explosion-size", 0);
smallFireball = getConfigBoolean("small-fireball", false);
noExplosion = config.getBoolean("spells." + spellName + ".no-explosion", false);
noExplosionEffect = getConfigBoolean("no-explosion-effect", true);
noExplosionDamage = getConfigInt("no-explosion-damage", 5);
noExplosionDamageRange = getConfigInt("no-explosion-damage-range", 3);
noFire = getConfigBoolean("no-fire", false);
fireballs = new HashMap<Fireball, Float>();
taskId = MagicSpells.scheduleRepeatingTask(new Runnable() {
public void run() {
Iterator<Map.Entry<Fireball, Float>> iter = fireballs.entrySet().iterator();
while (iter.hasNext()) {
if (iter.next().getKey().isDead()) {
iter.remove();
}
}
}
}, 60 * 20, 60 * 20);
}
@Override
public PostCastAction castSpell(Player player, SpellCastState state, float power, String[] args) {
if (state == SpellCastState.NORMAL) {
// get a target if required
boolean selfTarget = false;
if (requireEntityTarget) {
TargetInfo<LivingEntity> targetInfo = getTargetedEntity(player, power);
if (targetInfo == null) {
return noTarget(player);
}
LivingEntity entity = targetInfo.getTarget();
power = targetInfo.getPower();
if (entity == null) {
return noTarget(player);
} else if (entity instanceof Player && checkPlugins) {
// run a pvp damage check
EntityDamageByEntityEvent event = new EntityDamageByEntityEvent(player, entity, DamageCause.ENTITY_ATTACK, (double)1);
Bukkit.getServer().getPluginManager().callEvent(event);
if (event.isCancelled()) {
return noTarget(player);
}
}
if (entity.equals(player)) {
selfTarget = true;
}
}
// create fireball
Location loc;
if (!selfTarget) {
loc = player.getEyeLocation().toVector().add(player.getLocation().getDirection().multiply(2)).toLocation(player.getWorld(), player.getLocation().getYaw(), player.getLocation().getPitch());
} else {
loc = player.getLocation().toVector().add(player.getLocation().getDirection().setY(0).multiply(2)).toLocation(player.getWorld(), player.getLocation().getYaw()+180, 0);
}
Fireball fireball;
if (smallFireball) {
//fireball = player.getWorld().spawn(loc, SmallFireball.class);
fireball = MagicSpells.getVolatileCodeHandler().shootSmallFireball(player);
player.getWorld().playEffect(player.getLocation(), Effect.GHAST_SHOOT, 0);
} else {
fireball = player.getWorld().spawn(loc, Fireball.class);
player.getWorld().playEffect(player.getLocation(), Effect.GHAST_SHOOT, 0);
fireballs.put(fireball, power);
}
fireball.setShooter(player);
playSpellEffects(EffectPosition.CASTER, player);
}
return PostCastAction.HANDLE_NORMALLY;
}
@Override
public boolean castAtEntityFromLocation(Player caster, Location from, LivingEntity target, float power) {
Vector facing = target.getLocation().toVector().subtract(from.toVector()).normalize();
Location loc = from.clone();
Util.setLocationFacingFromVector(loc, facing);
loc.add(facing.multiply(2));
Fireball fireball = from.getWorld().spawn(loc, Fireball.class);
if (caster != null) {
fireball.setShooter(caster);
}
fireballs.put(fireball, power);
if (caster != null) {
playSpellEffects(EffectPosition.CASTER, caster);
} else {
playSpellEffects(EffectPosition.CASTER, from);
}
return true;
}
@Override
public boolean castAtEntityFromLocation(Location from, LivingEntity target, float power) {
return castAtEntityFromLocation(null, from, target, power);
}
@EventHandler(priority=EventPriority.HIGH)
public void onExplosionPrime(ExplosionPrimeEvent event) {
if (event.isCancelled()) {
return;
}
if (event.getEntity() instanceof Fireball) {
final Fireball fireball = (Fireball)event.getEntity();
if (fireballs.containsKey(fireball)) {
playSpellEffects(EffectPosition.TARGET, fireball.getLocation());
if (noExplosion) {
event.setCancelled(true);
Location loc = fireball.getLocation();
if (noExplosionEffect) {
loc.getWorld().createExplosion(loc, 0);
}
if (noExplosionDamage > 0) {
float power = fireballs.get(fireball);
List<Entity> inRange = fireball.getNearbyEntities(noExplosionDamageRange, noExplosionDamageRange, noExplosionDamageRange);
for (Entity entity : inRange) {
if (entity instanceof LivingEntity) {
if (validTargetList.canTarget((LivingEntity)entity)) {
((LivingEntity)entity).damage(Math.round(noExplosionDamage * power), (LivingEntity)fireball.getShooter());
}
}
}
}
if (!noFire) {
final HashSet<Block> fires = new HashSet<Block>();
for (int x = loc.getBlockX()-1; x <= loc.getBlockX()+1; x++) {
for (int y = loc.getBlockY()-1; y <= loc.getBlockY()+1; y++) {
for (int z = loc.getBlockZ()-1; z <= loc.getBlockZ()+1; z++) {
if (loc.getWorld().getBlockAt(x,y,z).getType() == Material.AIR) {
Block b = loc.getWorld().getBlockAt(x,y,z);
BlockUtils.setTypeAndData(b, Material.FIRE, (byte)15, false);
fires.add(b);
}
}
}
}
fireball.remove();
if (fires.size() > 0) {
Bukkit.getServer().getScheduler().scheduleSyncDelayedTask(MagicSpells.plugin, new Runnable() {
@Override
public void run() {
for (Block b : fires) {
if (b.getType() == Material.FIRE) {
b.setType(Material.AIR);
}
}
}
}, 20);
}
}
} else {
if (noFire) {
event.setFire(false);
} else {
event.setFire(true);
}
if (explosionSize > 0) {
event.setRadius(explosionSize);
}
}
if (noExplosion) {
// remove immediately
fireballs.remove(fireball);
} else {
// schedule removal (gotta wait for damage events)
Bukkit.getScheduler().scheduleSyncDelayedTask(MagicSpells.plugin, new Runnable() {
public void run() {
fireballs.remove(fireball);
}
}, 1);
}
}
}
}
@EventHandler(priority=EventPriority.HIGH, ignoreCancelled=true)
public void onEntityDamage(EntityDamageEvent event) {
if (event.getEntity() instanceof LivingEntity && event instanceof EntityDamageByEntityEvent && (event.getCause() == DamageCause.ENTITY_EXPLOSION || event.getCause() == DamageCause.PROJECTILE)) {
EntityDamageByEntityEvent evt = (EntityDamageByEntityEvent)event;
if (evt.getDamager() instanceof Fireball || evt.getDamager() instanceof SmallFireball) {
Fireball fireball = (Fireball)evt.getDamager();
if (fireball.getShooter() instanceof Player && fireballs.containsKey(fireball)) {
float power = fireballs.get(fireball);
if (!validTargetList.canTarget((Player)fireball.getShooter(), (LivingEntity)event.getEntity())) {
event.setCancelled(true);
} else if (damageMultiplier > 0) {
event.setDamage(Math.round(event.getDamage() * damageMultiplier * power));
}
}
}
}
}
@Override
public void turnOff() {
MagicSpells.cancelTask(taskId);
}
}