package com.nisovin.magicspells.spells.instant;
import java.util.HashMap;
import java.util.Map;
import org.bukkit.Bukkit;
import org.bukkit.Location;
import org.bukkit.entity.Player;
import org.bukkit.event.EventHandler;
import org.bukkit.event.EventPriority;
import org.bukkit.event.Listener;
import org.bukkit.event.player.PlayerMoveEvent;
import com.nisovin.magicspells.MagicSpells;
import com.nisovin.magicspells.Spell;
import com.nisovin.magicspells.events.SpellTargetEvent;
import com.nisovin.magicspells.spelleffects.EffectPosition;
import com.nisovin.magicspells.spells.InstantSpell;
import com.nisovin.magicspells.util.BoundingBox;
import com.nisovin.magicspells.util.MagicConfig;
import com.nisovin.magicspells.util.MagicLocation;
import com.nisovin.magicspells.util.SpellReagents;
public class PortalSpell extends InstantSpell {
private String markSpellName;
private int duration;
private int teleportCooldown;
private int minDistanceSq;
private int maxDistanceSq;
private int effectInterval;
private SpellReagents teleportCost;
private boolean allowReturn;
private boolean chargeCostToTeleporter;
private String strNoMark;
private String strTooClose;
private String strTooFar;
private String strTeleportCostFail;
private String strTeleportCooldownFail;
private HashMap<String, MagicLocation> marks;
public PortalSpell(MagicConfig config, String spellName) {
super(config, spellName);
markSpellName = getConfigString("mark-spell", "mark");
duration = getConfigInt("duration", 400);
teleportCooldown = getConfigInt("teleport-cooldown", 5) * 1000;
minDistanceSq = getConfigInt("min-distance", 10);
minDistanceSq *= minDistanceSq;
maxDistanceSq = getConfigInt("max-distance", 0);
maxDistanceSq *= maxDistanceSq;
effectInterval = getConfigInt("effect-interval", 10);
teleportCost = getConfigReagents("teleport-cost");
allowReturn = getConfigBoolean("allow-return", true);
chargeCostToTeleporter = getConfigBoolean("charge-cost-to-teleporter", false);
strNoMark = getConfigString("str-no-mark", "You have not marked a location to make a portal to.");
strTooClose = getConfigString("str-too-close", "You are too close to your marked location.");
strTooFar = getConfigString("str-too-far", "You are too far away from your marked location.");
strTeleportCostFail = getConfigString("str-teleport-cost-fail", "");
strTeleportCooldownFail = getConfigString("str-teleport-cooldown-fail", "");
}
@Override
public void initialize() {
super.initialize();
Spell spell = MagicSpells.getSpellByInternalName(markSpellName);
if (spell != null && spell instanceof MarkSpell) {
marks = ((MarkSpell)spell).getMarks();
} else {
MagicSpells.error("Failed to get marks list for '" + internalName + "' spell");
}
}
@Override
public PostCastAction castSpell(Player player, SpellCastState state, float power, String[] args) {
if (state == SpellCastState.NORMAL) {
Location loc = null;
MagicLocation mark = marks.get(player.getName().toLowerCase());
if (mark != null) {
loc = mark.getLocation();
}
if (loc == null) {
// no mark
sendMessage(player, strNoMark);
return PostCastAction.ALREADY_HANDLED;
} else {
Location playerLoc = player.getLocation();
double distanceSq = 0;
if (maxDistanceSq > 0) {
if (!loc.getWorld().equals(playerLoc.getWorld())) {
sendMessage(player, strTooFar);
return PostCastAction.ALREADY_HANDLED;
} else {
distanceSq = playerLoc.distanceSquared(loc);
if (distanceSq > maxDistanceSq) {
sendMessage(player, strTooFar);
return PostCastAction.ALREADY_HANDLED;
}
}
}
if (minDistanceSq > 0) {
if (loc.getWorld().equals(playerLoc.getWorld())) {
if (distanceSq == 0) distanceSq = playerLoc.distanceSquared(loc);
if (distanceSq < minDistanceSq) {
sendMessage(player, strTooClose);
return PostCastAction.ALREADY_HANDLED;
}
}
}
new PortalLink(this, player, playerLoc, loc);
playSpellEffects(EffectPosition.CASTER, player);
}
}
return PostCastAction.HANDLE_NORMALLY;
}
@Override
public boolean isBeneficialDefault() {
return true;
}
class PortalLink implements Listener {
PortalSpell spell;
Player caster;
Location loc1;
Location loc2;
BoundingBox box1;
BoundingBox box2;
int taskId1 = -1;
int taskId2 = -1;
Map<String, Long> cooldownUntil = new HashMap<String, Long>();
public PortalLink (PortalSpell spell, Player caster, Location loc1, Location loc2) {
this.spell = spell;
this.caster = caster;
this.loc1 = loc1;
this.loc2 = loc2;
this.box1 = new BoundingBox(loc1, .6);
this.box2 = new BoundingBox(loc2, .6);
cooldownUntil.put(caster.getName(), System.currentTimeMillis() + teleportCooldown);
registerEvents(this);
startTasks();
}
void startTasks() {
if (effectInterval > 0) {
taskId1 = MagicSpells.scheduleRepeatingTask(new Runnable() {
public void run() {
if (caster.isValid()) {
playSpellEffects(EffectPosition.SPECIAL, loc1);
playSpellEffects(EffectPosition.SPECIAL, loc2);
} else {
disable();
}
}
}, effectInterval, effectInterval);
}
taskId2 = MagicSpells.scheduleDelayedTask(new Runnable() {
public void run() {
disable();
}
}, duration);
}
@EventHandler(priority=EventPriority.NORMAL, ignoreCancelled=true)
void onMove(PlayerMoveEvent event) {
if (caster.isValid()) {
Player player = event.getPlayer();
if (box1.contains(event.getTo())) {
if (checkTeleport(player)) {
Location loc = loc2.clone();
loc.setYaw(player.getLocation().getYaw());
loc.setPitch(player.getLocation().getPitch());
event.setTo(loc);
playSpellEffects(EffectPosition.TARGET, player);
}
} else if (allowReturn && box2.contains(event.getTo())) {
if (checkTeleport(player)) {
Location loc = loc1.clone();
loc.setYaw(player.getLocation().getYaw());
loc.setPitch(player.getLocation().getPitch());
event.setTo(loc);
playSpellEffects(EffectPosition.TARGET, player);
}
}
} else {
disable();
}
}
boolean checkTeleport(Player player) {
if (cooldownUntil.containsKey(player.getName()) && cooldownUntil.get(player.getName()) > System.currentTimeMillis()) {
sendMessage(player, strTeleportCooldownFail);
return false;
}
cooldownUntil.put(player.getName(), System.currentTimeMillis() + teleportCooldown);
Player payer = null;
if (teleportCost != null) {
if (chargeCostToTeleporter) {
if (hasReagents(player, teleportCost)) {
payer = player;
} else {
sendMessage(player, strTeleportCostFail);
return false;
}
} else {
if (hasReagents(caster, teleportCost)) {
payer = caster;
} else {
sendMessage(player, strTeleportCostFail);
return false;
}
}
if (payer == null) return false;
}
SpellTargetEvent event = new SpellTargetEvent(spell, caster, player, 1);
Bukkit.getPluginManager().callEvent(event);
if (event.isCancelled()) {
return false;
}
if (payer != null) {
removeReagents(payer, teleportCost);
}
return true;
}
void disable() {
playSpellEffects(EffectPosition.DELAYED, loc1);
playSpellEffects(EffectPosition.DELAYED, loc2);
unregisterEvents(this);
if (taskId1 > 0) {
MagicSpells.cancelTask(taskId1);
}
if (taskId2 > 0) {
MagicSpells.cancelTask(taskId2);
}
spell = null;
caster = null;
loc1 = null;
loc2 = null;
box1 = null;
box2 = null;
cooldownUntil.clear();
cooldownUntil = null;
}
}
}