package com.nisovin.magicspells.spells;
import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.Random;
import org.bukkit.Bukkit;
import org.bukkit.ChatColor;
import org.bukkit.Location;
import org.bukkit.Material;
import org.bukkit.block.Block;
import org.bukkit.command.CommandSender;
import org.bukkit.entity.LivingEntity;
import org.bukkit.entity.Player;
import org.bukkit.event.EventHandler;
import org.bukkit.event.inventory.ClickType;
import org.bukkit.event.inventory.InventoryClickEvent;
import org.bukkit.event.player.PlayerQuitEvent;
import org.bukkit.inventory.Inventory;
import org.bukkit.inventory.ItemStack;
import org.bukkit.inventory.meta.ItemMeta;
import com.nisovin.magicspells.MagicSpells;
import com.nisovin.magicspells.Subspell;
import com.nisovin.magicspells.castmodifiers.ModifierSet;
import com.nisovin.magicspells.events.MagicSpellsGenericPlayerEvent;
import com.nisovin.magicspells.spelleffects.EffectPosition;
import com.nisovin.magicspells.util.MagicConfig;
import com.nisovin.magicspells.util.TargetInfo;
import com.nisovin.magicspells.util.Util;
public class MenuSpell extends TargetedSpell implements TargetedEntitySpell, TargetedLocationSpell {
Random random = new Random();
String title;
int delay;
boolean requireEntityTarget;
boolean requireLocationTarget;
boolean targetOpensMenuInstead;
boolean bypassNormalCast;
boolean uniqueNames;
Map<String, MenuOption> options = new LinkedHashMap<String, MenuOption>();
int size = 9;
Map<String, Float> castPower = new HashMap<String, Float>();
Map<String, LivingEntity> castEntityTarget = new HashMap<String, LivingEntity>();
Map<String, Location> castLocTarget = new HashMap<String, Location>();
public MenuSpell(MagicConfig config, String spellName) {
super(config, spellName);
title = ChatColor.translateAlternateColorCodes('&', getConfigString("title", "Window Title " + spellName));
delay = getConfigInt("delay", 0);
requireEntityTarget = getConfigBoolean("require-entity-target", false);
requireLocationTarget = getConfigBoolean("require-location-target", false);
targetOpensMenuInstead = getConfigBoolean("target-opens-menu-instead", false);
bypassNormalCast = getConfigBoolean("bypass-normal-cast", true);
uniqueNames = getConfigBoolean("unique-names", false);
int maxSlot = 8;
for (String optionName : getConfigKeys("options")) {
int optionSlot = getConfigInt("options." + optionName + ".slot", -1);
String optionSpellName = getConfigString("options." + optionName + ".spell", "");
float optionPower = getConfigFloat("options." + optionName + ".power", 1);
ItemStack optionItem;
if (isConfigSection("options." + optionName + ".item")) {
optionItem = Util.getItemStackFromConfig(getConfigSection("options." + optionName + ".item"));
} else {
optionItem = Util.getItemStackFromString(getConfigString("options." + optionName + ".item", "stone"));
}
int optionQuantity = getConfigInt("options." + optionName + ".quantity", 1);
List<String> modifierList = getConfigStringList("options." + optionName + ".modifiers", null);
boolean optionStayOpen = getConfigBoolean("options." + optionName + ".stay-open", false);
if (optionSlot >= 0 && !optionSpellName.isEmpty() && optionItem != null) {
optionItem.setAmount(optionQuantity);
Util.setLoreData(optionItem, optionName);
MenuOption option = new MenuOption();
option.slot = optionSlot;
option.name = optionName;
option.spellName = optionSpellName;
option.power = optionPower;
option.item = optionItem;
option.modifierList = modifierList;
option.stayOpen = optionStayOpen;
String optionKey = uniqueNames ? getOptionKey(option.item) : optionName;
options.put(optionKey, option);
if (optionSlot > maxSlot) {
maxSlot = optionSlot;
}
}
}
size = ((maxSlot / 9) * 9) + 9;
if (options.size() == 0) {
MagicSpells.error("The MenuSpell '" + spellName + "' has no menu options!");
}
}
@Override
public void initialize() {
super.initialize();
for (MenuOption option : options.values()) {
Subspell spell = new Subspell(option.spellName);
if (spell.process()) {
option.spell = spell;
if (option.modifierList != null) {
option.modifiers = new ModifierSet(option.modifierList);
}
} else {
MagicSpells.error("The MenuSpell '" + internalName + "' has an invalid spell listed on '" + option.name + "'");
}
}
}
@Override
public PostCastAction castSpell(Player player, SpellCastState state, float power, String[] args) {
if (state == SpellCastState.NORMAL) {
LivingEntity entityTarget = null;
Location locTarget = null;
Player opener = player;
if (requireEntityTarget) {
TargetInfo<LivingEntity> targetInfo = getTargetedEntity(player, power);
if (targetInfo != null) {
entityTarget = targetInfo.getTarget();
}
if (entityTarget == null) {
return noTarget(player);
}
if (targetOpensMenuInstead) {
if (entityTarget instanceof Player) {
opener = (Player)entityTarget;
entityTarget = null;
} else {
return noTarget(player);
}
}
} else if (requireLocationTarget) {
Block block = getTargetedBlock(player, power);
if (block == null || block.getType() == Material.AIR) {
return noTarget(player);
} else {
locTarget = block.getLocation();
}
}
open(player, opener, entityTarget, locTarget, power);
}
return PostCastAction.HANDLE_NORMALLY;
}
String getOptionKey(ItemStack item) {
return item.getType().name() + "_" + item.getDurability() + "_" + item.getItemMeta().getDisplayName();
}
void open(final Player caster, Player opener, LivingEntity entityTarget, Location locTarget, final float power) {
if (delay < 0) {
openMenu(caster, opener, entityTarget, locTarget, power);
} else {
final Player p = opener;
final LivingEntity e = entityTarget;
final Location l = locTarget;
MagicSpells.scheduleDelayedTask(new Runnable() {
public void run() {
openMenu(caster, p, e, l, power);
}
}, delay);
}
}
void openMenu(Player caster, Player opener, LivingEntity entityTarget, Location locTarget, float power) {
castPower.put(opener.getName(), power);
if (requireEntityTarget && entityTarget != null) {
castEntityTarget.put(opener.getName(), entityTarget);
}
if (requireLocationTarget && locTarget != null) {
castLocTarget.put(opener.getName(), locTarget);
}
Inventory inv = Bukkit.createInventory(opener, size, title);
applyOptionsToInventory(opener, inv);
opener.openInventory(inv);
if (entityTarget != null && caster != null) {
playSpellEffects(caster, entityTarget);
} else {
if (caster != null) {
playSpellEffects(EffectPosition.CASTER, caster);
}
playSpellEffects(EffectPosition.SPECIAL, opener);
if (locTarget != null) {
playSpellEffects(EffectPosition.TARGET, locTarget);
}
}
}
void applyOptionsToInventory(Player opener, Inventory inv) {
inv.clear();
for (MenuOption option : options.values()) {
if (option.spell != null && inv.getItem(option.slot) == null) {
if (option.modifiers != null) {
MagicSpellsGenericPlayerEvent event = new MagicSpellsGenericPlayerEvent(opener);
option.modifiers.apply(event);
if (event.isCancelled()) continue;
}
ItemStack item = option.item.clone();
ItemMeta meta = item.getItemMeta();
meta.setDisplayName(MagicSpells.doVariableReplacements(opener, meta.getDisplayName()));
List<String> lore = meta.getLore();
if (lore != null && lore.size() > 1) {
for (int i = 0; i < lore.size() - 1; i++) {
lore.set(i, MagicSpells.doVariableReplacements(opener, lore.get(i)));
}
meta.setLore(lore);
}
item.setItemMeta(meta);
inv.setItem(option.slot, item);
}
}
}
@EventHandler
public void onInvClick(InventoryClickEvent event) {
if (event.getInventory().getTitle().equals(title)) {
event.setCancelled(true);
if (event.getClick() == ClickType.LEFT) {
final Player player = (Player)event.getWhoClicked();
boolean close = true;
ItemStack item = event.getCurrentItem();
if (item != null) {
String key = uniqueNames ? getOptionKey(item) : Util.getLoreData(item);
if (key != null && !key.isEmpty() && options.containsKey(key)) {
MenuOption option = options.get(key);
Subspell spell = option.spell;
if (spell != null) {
float power = option.power;
if (castPower.containsKey(player.getName())) {
power *= castPower.get(player.getName()).floatValue();
}
if (spell.isTargetedEntitySpell() && castEntityTarget.containsKey(player.getName())) {
spell.castAtEntity(player, castEntityTarget.get(player.getName()), power);
} else if (spell.isTargetedLocationSpell() && castLocTarget.containsKey(player.getName())) {
spell.castAtLocation(player, castLocTarget.get(player.getName()), power);
} else if (bypassNormalCast) {
spell.cast(player, power);
} else {
spell.getSpell().cast(player, power, null);
}
}
if (option.stayOpen) close = false;
}
}
castPower.remove(player.getName());
castEntityTarget.remove(player.getName());
castLocTarget.remove(player.getName());
if (close) {
MagicSpells.scheduleDelayedTask(new Runnable() {
public void run() {
player.closeInventory();
}
}, 0);
} else {
applyOptionsToInventory(player, event.getView().getTopInventory());
}
}
}
}
@EventHandler
public void onQuit(PlayerQuitEvent event) {
castPower.remove(event.getPlayer().getName());
castEntityTarget.remove(event.getPlayer().getName());
castLocTarget.remove(event.getPlayer().getName());
}
@Override
public boolean castAtEntity(Player caster, LivingEntity target, float power) {
if (requireEntityTarget && !validTargetList.canTarget(caster, target)) return false;
Player opener = caster;
if (targetOpensMenuInstead) {
if (target instanceof Player) {
opener = (Player)target;
target = null;
} else {
return false;
}
}
open(caster, opener, target, null, power);
return true;
}
@Override
public boolean castAtEntity(LivingEntity target, float power) {
if (!targetOpensMenuInstead) return false;
if (requireEntityTarget && !validTargetList.canTarget(target)) return false;
if (target instanceof Player) {
open(null, (Player)target, null, null, power);
return true;
} else {
return false;
}
}
@Override
public boolean castAtLocation(Player caster, Location target, float power) {
open(caster, caster, null, target, power);
return true;
}
@Override
public boolean castAtLocation(Location target, float power) {
return false;
}
@Override
public boolean castFromConsole(CommandSender sender, String[] args) {
if (args.length == 1) {
Player player = Bukkit.getPlayer(args[0]);
if (player != null) {
open(null, player, null, null, 1);
return true;
}
}
return false;
}
class MenuOption {
String name;
int slot;
ItemStack item;
String spellName;
Subspell spell;
float power;
List<String> modifierList;
ModifierSet modifiers;
boolean stayOpen;
}
}