package com.nisovin.magicspells;
import java.io.BufferedWriter;
import java.io.File;
import java.io.FileWriter;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Scanner;
import java.util.Set;
import java.util.TreeSet;
import org.bukkit.Bukkit;
import org.bukkit.World;
import org.bukkit.entity.Player;
import org.bukkit.inventory.ItemStack;
import org.bukkit.plugin.Plugin;
import com.nisovin.magicspells.events.SpellSelectionChangedEvent;
import com.nisovin.magicspells.spells.BuffSpell;
import com.nisovin.magicspells.util.CastItem;
import com.nisovin.magicspells.util.Util;
public class Spellbook {
private MagicSpells plugin;
private Player player;
private String playerName;
private String uniqueId;
private TreeSet<Spell> allSpells = new TreeSet<Spell>();
private HashMap<CastItem, ArrayList<Spell>> itemSpells = new HashMap<CastItem,ArrayList<Spell>>();
private HashMap<CastItem, Integer> activeSpells = new HashMap<CastItem,Integer>();
private HashMap<Spell, Set<CastItem>> customBindings = new HashMap<Spell,Set<CastItem>>();
private HashMap<Plugin, Set<Spell>> temporarySpells = new HashMap<Plugin,Set<Spell>>();
private Set<String> cantLearn = new HashSet<String>();
public Spellbook(Player player, MagicSpells plugin) {
this.plugin = plugin;
this.player = player;
this.playerName = player.getName();
this.uniqueId = Util.getUniqueId(player);
MagicSpells.debug(1, "Loading player spell list: " + playerName);
load();
}
public void destroy() {
player = null;
playerName = null;
allSpells.clear();
itemSpells.clear();
activeSpells.clear();
customBindings.clear();
temporarySpells.clear();
cantLearn.clear();
}
public void load() {
load(player.getWorld());
}
public void load(World playerWorld) {
// load spells from file
loadFromFile(playerWorld);
// give all spells to ops, or if ignoring grant perms
if (plugin.ignoreGrantPerms || (player.isOp() && plugin.opsHaveAllSpells)) {
MagicSpells.debug(2, " Op, granting all spells...");
for (Spell spell : plugin.spellsOrdered) {
if (!spell.isHelperSpell() && !allSpells.contains(spell)) {
addSpell(spell);
}
}
}
// add spells granted by permissions
if (!plugin.ignoreGrantPerms) {
addGrantedSpells();
}
// sort spells or pre-select if just one
for (CastItem i : itemSpells.keySet()) {
ArrayList<Spell> spells = itemSpells.get(i);
if (spells.size() == 1 && !plugin.allowCycleToNoSpell) {
activeSpells.put(i, 0);
} else {
Collections.sort(spells);
}
}
}
private void loadFromFile(World playerWorld) {
try {
MagicSpells.debug(2, " Loading spells from player file...");
File file;
if (plugin.separatePlayerSpellsPerWorld) {
File folder = new File(plugin.getDataFolder(), "spellbooks" + File.separator + player.getWorld().getName());
if (!folder.exists()) {
folder.mkdir();
}
file = new File(plugin.getDataFolder(), "spellbooks" + File.separator + playerWorld.getName() + File.separator + uniqueId + ".txt");
if (!file.exists()) {
File file2 = new File(plugin.getDataFolder(), "spellbooks" + File.separator + playerWorld.getName() + File.separator + playerName.toLowerCase() + ".txt");
if (file2.exists()) {
file2.renameTo(file);
}
}
} else {
file = new File(plugin.getDataFolder(), "spellbooks" + File.separator + uniqueId + ".txt");
if (!file.exists()) {
File file2 = new File(plugin.getDataFolder(), "spellbooks" + File.separator + playerName.toLowerCase() + ".txt");
if (file2.exists()) {
file2.renameTo(file);
}
}
}
if (file.exists()) {
Scanner scanner = new Scanner(file);
while (scanner.hasNext()) {
String line = scanner.nextLine();
if (!line.equals("")) {
if (!line.contains(":")) {
Spell spell = MagicSpells.getSpellByInternalName(line);
if (spell != null) {
addSpell(spell);
}
} else {
String[] data = line.split(":", 2);
Spell spell = MagicSpells.getSpellByInternalName(data[0]);
if (spell != null) {
ArrayList<CastItem> items = new ArrayList<CastItem>();
String[] s = data[1].split(",");
for (int i = 0; i < s.length; i++) {
try {
CastItem castItem = new CastItem(s[i]);
items.add(castItem);
} catch (Exception e) {
e.printStackTrace();
}
}
addSpell(spell, items.toArray(new CastItem[items.size()]));
}
}
}
}
scanner.close();
}
} catch (Exception e) {
}
}
public void addGrantedSpells() {
MagicSpells.debug(2, " Adding granted spells...");
boolean added = false;
for (Spell spell : plugin.spellsOrdered) {
MagicSpells.debug(3, " Checking spell " + spell.getInternalName() + "...");
if (!spell.isHelperSpell() && !hasSpell(spell, false)) {
if (spell.isAlwaysGranted() || player.hasPermission("magicspells.grant." + spell.getPermissionName())) {
addSpell(spell);
added = true;
}
}
}
if (added) {
save();
}
}
public boolean canLearn(Spell spell) {
if (spell.isHelperSpell()) return false;
if (cantLearn.contains(spell.getInternalName().toLowerCase())) return false;
if (spell.prerequisites != null) {
for (String spellName : spell.prerequisites) {
Spell sp = MagicSpells.getSpellByInternalName(spellName);
if (sp == null || !hasSpell(sp)) {
return false;
}
}
}
if (spell.xpRequired != null) {
MagicXpHandler handler = MagicSpells.getMagicXpHandler();
if (handler != null) {
for (String school : spell.xpRequired.keySet()) {
if (handler.getXp(player, school) < spell.xpRequired.get(school)) {
return false;
}
}
}
}
return player.hasPermission("magicspells.learn." + spell.getPermissionName());
}
public boolean canCast(Spell spell) {
if (spell.isHelperSpell()) return true;
return plugin.ignoreCastPerms || player.hasPermission("magicspells.cast." + spell.getPermissionName());
}
public boolean canTeach(Spell spell) {
if (spell.isHelperSpell()) return false;
return player.hasPermission("magicspells.teach." + spell.getPermissionName());
}
public boolean hasAdvancedPerm(String spell) {
return player.hasPermission("magicspells.advanced." + spell);
}
public Spell getSpellByName(String spellName) {
Spell spell = MagicSpells.getSpellByInGameName(spellName);
if (spell != null && hasSpell(spell)) {
return spell;
} else {
return null;
}
}
public Set<Spell> getSpells() {
return this.allSpells;
}
public List<String> tabComplete(String partial) {
String[] data = Util.splitParams(partial, 2);
if (data.length == 1) {
// complete spell name
partial = data[0].toLowerCase();
List<String> options = new ArrayList<String>();
for (Spell spell : allSpells) {
if (spell.canCastByCommand() && !spell.isHelperSpell()) {
if (spell.getName().toLowerCase().startsWith(partial)) {
options.add(spell.getName());
} else {
String[] aliases = spell.getAliases();
if (aliases != null && aliases.length > 0) {
for (String alias : aliases) {
if (alias.toLowerCase().startsWith(partial)) {
options.add(alias);
}
}
}
}
}
}
if (options.size() > 0) {
return options;
} else {
return null;
}
} else {
// complete spell params
Spell spell = getSpellByName(data[0]);
if (spell == null) {
return null;
} else {
List<String> ret = spell.tabComplete(player, data[1]);
if (ret == null || ret.size() == 0) {
return null;
} else {
return ret;
}
}
}
}
protected CastItem getCastItemForCycling(ItemStack item) {
CastItem castItem;
if (item != null) {
castItem = new CastItem(item);
} else {
castItem = new CastItem(0);
}
ArrayList<Spell> spells = itemSpells.get(castItem);
if (spells != null && (spells.size() > 1 || (spells.size() == 1 && plugin.allowCycleToNoSpell))) {
return castItem;
} else {
return null;
}
}
protected Spell nextSpell(ItemStack item) {
CastItem castItem = getCastItemForCycling(item);
if (castItem != null) {
return nextSpell(castItem);
}
return null;
}
protected Spell nextSpell(CastItem castItem) {
Integer i = activeSpells.get(castItem); // get the index of the active spell for the cast item
if (i != null) {
ArrayList<Spell> spells = itemSpells.get(castItem); // get all the spells for the cast item
if (spells.size() > 1 || i.equals(-1) || plugin.allowCycleToNoSpell || plugin.alwaysShowMessageOnCycle) {
int count = 0;
while (count++ < spells.size()) {
i++;
if (i >= spells.size()) {
if (plugin.allowCycleToNoSpell) {
activeSpells.put(castItem, -1);
Bukkit.getPluginManager().callEvent(new SpellSelectionChangedEvent(null, player, castItem, this));
MagicSpells.sendMessage(player, plugin.strSpellChangeEmpty);
return null;
} else {
i = 0;
}
}
if (!plugin.onlyCycleToCastableSpells || canCast(spells.get(i))) {
activeSpells.put(castItem, i);
Bukkit.getPluginManager().callEvent(new SpellSelectionChangedEvent(spells.get(i), player, castItem, this));
return spells.get(i);
}
}
return null;
} else {
return null;
}
} else {
return null;
}
}
protected Spell prevSpell(ItemStack item) {
CastItem castItem = getCastItemForCycling(item);
if (castItem != null) {
return prevSpell(castItem);
}
return null;
}
protected Spell prevSpell(CastItem castItem) {
Integer i = activeSpells.get(castItem); // get the index of the active spell for the cast item
if (i != null) {
ArrayList<Spell> spells = itemSpells.get(castItem); // get all the spells for the cast item
if (spells.size() > 1 || i.equals(-1) || plugin.allowCycleToNoSpell) {
int count = 0;
while (count++ < spells.size()) {
i--;
if (i < 0) {
if (plugin.allowCycleToNoSpell && i == -1) {
activeSpells.put(castItem, -1);
Bukkit.getPluginManager().callEvent(new SpellSelectionChangedEvent(null, player, castItem, this));
MagicSpells.sendMessage(player, plugin.strSpellChangeEmpty);
return null;
} else {
i = spells.size() - 1;
}
}
if (!plugin.onlyCycleToCastableSpells || canCast(spells.get(i))) {
activeSpells.put(castItem, i);
Bukkit.getPluginManager().callEvent(new SpellSelectionChangedEvent(spells.get(i), player, castItem, this));
return spells.get(i);
}
}
return null;
} else {
return null;
}
} else {
return null;
}
}
public Spell getActiveSpell(ItemStack item) {
CastItem castItem = new CastItem(item);
return getActiveSpell(castItem);
}
public Spell getActiveSpell(CastItem castItem) {
Integer i = activeSpells.get(castItem);
if (i != null && i != -1) {
return itemSpells.get(castItem).get(i);
} else {
return null;
}
}
public boolean hasSpell(Spell spell) {
return hasSpell(spell, true);
}
public boolean hasSpell(Spell spell, boolean checkGranted) {
if (plugin.ignoreGrantPerms) return true;
boolean has = allSpells.contains(spell);
if (has) {
return true;
} else if (checkGranted && player.hasPermission("magicspells.grant." + spell.getPermissionName())) {
MagicSpells.debug(2, "Adding granted spell for " + player.getName() + ": " + spell.getName());
addSpell(spell);
save();
return true;
} else if (player.hasPermission("magicspells.tempgrant." + spell.getPermissionName())) {
return true;
} else {
return false;
}
}
public void addSpell(Spell spell) {
addSpell(spell, (CastItem[])null);
}
public void addSpell(Spell spell, CastItem castItem) {
addSpell(spell, new CastItem[] {castItem});
}
public void addSpell(Spell spell, CastItem[] castItems) {
if (spell == null) return;
MagicSpells.debug(3, " Added spell: " + spell.getInternalName());
allSpells.add(spell);
if (spell.canCastWithItem()) {
CastItem[] items = spell.getCastItems();
if (castItems != null && castItems.length > 0) {
items = castItems;
HashSet<CastItem> set = new HashSet<CastItem>();
for (CastItem item : items) {
if (item != null) {
set.add(item);
}
}
customBindings.put(spell, set);
} else if (plugin.ignoreDefaultBindings) {
return; // no cast item provided and ignoring default, so just stop here
}
for (CastItem i : items) {
MagicSpells.debug(3, " Cast item: " + i + (castItems!=null?" (custom)":" (default)"));
if (i != null) {
ArrayList<Spell> temp = itemSpells.get(i);
if (temp != null) {
temp.add(spell);
} else {
temp = new ArrayList<Spell>();
temp.add(spell);
itemSpells.put(i, temp);
activeSpells.put(i, plugin.allowCycleToNoSpell ? -1 : 0);
}
}
}
}
// remove any spells that this spell replaces
if (spell.replaces != null) {
for (String spellName : spell.replaces) {
Spell sp = MagicSpells.getSpellByInternalName(spellName);
if (sp != null) {
MagicSpells.debug(3, " Removing replaced spell: " + sp.getInternalName());
removeSpell(sp);
}
}
}
// prevent learning of spells this spell precludes
if (spell.precludes != null) {
for (String s : spell.precludes) {
cantLearn.add(s.toLowerCase());
}
}
}
public void removeSpell(Spell spell) {
if (spell instanceof BuffSpell) {
((BuffSpell)spell).turnOff(player);
}
CastItem[] items = spell.getCastItems();
if (customBindings.containsKey(spell)) {
items = customBindings.remove(spell).toArray(new CastItem[]{});
}
for (CastItem item : items) {
if (item != null) {
ArrayList<Spell> temp = itemSpells.get(item);
if (temp != null) {
temp.remove(spell);
if (temp.size() == 0) {
itemSpells.remove(item);
activeSpells.remove(item);
} else {
activeSpells.put(item, -1);
}
}
}
}
allSpells.remove(spell);
}
public void addTemporarySpell(Spell spell, Plugin plugin) {
if (!hasSpell(spell)) {
addSpell(spell);
Set<Spell> temps = temporarySpells.get(plugin);
if (temps == null) {
temps = new HashSet<Spell>();
temporarySpells.put(plugin, temps);
}
temps.add(spell);
}
}
public void removeTemporarySpells(Plugin plugin) {
Set<Spell> temps = temporarySpells.remove(plugin);
if (temps != null) {
for (Spell spell : temps) {
removeSpell(spell);
}
}
}
private boolean isTemporary(Spell spell) {
for (Set<Spell> temps : temporarySpells.values()) {
if (temps.contains(spell)) {
return true;
}
}
return false;
}
public void addCastItem(Spell spell, CastItem castItem) {
// add to custom bindings
Set<CastItem> bindings = customBindings.get(spell);
if (bindings == null) {
bindings = new HashSet<CastItem>();
customBindings.put(spell, bindings);
}
if (!bindings.contains(castItem)) {
bindings.add(castItem);
}
// add to item bindings
ArrayList<Spell> bindList = itemSpells.get(castItem);
if (bindList == null) {
bindList = new ArrayList<Spell>();
itemSpells.put(castItem, bindList);
activeSpells.put(castItem, plugin.allowCycleToNoSpell ? -1 : 0);
}
bindList.add(spell);
}
public boolean removeCastItem(Spell spell, CastItem castItem) {
boolean removed = false;
// remove from custom bindings
Set<CastItem> bindings = customBindings.get(spell);
if (bindings != null) {
removed = bindings.remove(castItem);
if (bindings.size() == 0) {
bindings.add(new CastItem(-1));
}
}
// remove from active bindings
ArrayList<Spell> bindList = itemSpells.get(castItem);
if (bindList != null) {
removed = bindList.remove(spell) || removed;
if (bindList.size() == 0) {
itemSpells.remove(castItem);
activeSpells.remove(castItem);
} else {
activeSpells.put(castItem, -1);
}
}
return removed;
}
public void removeAllCustomBindings() {
customBindings.clear();
save();
reload();
}
public void removeAllSpells() {
for (Spell spell : allSpells) {
if (spell instanceof BuffSpell) {
((BuffSpell)spell).turnOff(player);
}
}
allSpells.clear();
itemSpells.clear();
activeSpells.clear();
customBindings.clear();
}
public void reload() {
MagicSpells.debug(1, "Reloading player spell list: " + playerName);
removeAllSpells();
load();
}
public void save() {
try {
File file;
if (plugin.separatePlayerSpellsPerWorld) {
File folder = new File(plugin.getDataFolder(), "spellbooks" + File.separator + player.getWorld().getName());
if (!folder.exists()) {
folder.mkdirs();
}
File oldfile = new File(plugin.getDataFolder(), "spellbooks" + File.separator + player.getWorld().getName() + File.separator + playerName + ".txt");
if (oldfile.exists()) oldfile.delete();
file = new File(plugin.getDataFolder(), "spellbooks" + File.separator + player.getWorld().getName() + File.separator + uniqueId + ".txt");
} else {
File oldfile = new File(plugin.getDataFolder(), "spellbooks" + File.separator + playerName + ".txt");
if (oldfile.exists()) oldfile.delete();
file = new File(plugin.getDataFolder(), "spellbooks" + File.separator + uniqueId + ".txt");
}
BufferedWriter writer = new BufferedWriter(new FileWriter(file, false));
for (Spell spell : allSpells) {
if (!isTemporary(spell)) {
writer.append(spell.getInternalName());
if (customBindings.containsKey(spell)) {
Set<CastItem> items = customBindings.get(spell);
String s = "";
for (CastItem i : items) {
s += (s.isEmpty()?"":",") + i;
}
writer.append(":" + s);
}
writer.newLine();
}
}
writer.close();
MagicSpells.debug(2, "Saved spellbook file: " + playerName.toLowerCase());
} catch (Exception e) {
plugin.getServer().getLogger().severe("Error saving player spellbook: " + playerName);
e.printStackTrace();
}
}
}