package com.nisovin.magicspells.spells.command;
import java.io.BufferedWriter;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileWriter;
import java.util.ArrayList;
import java.util.List;
import java.util.Scanner;
import org.bukkit.Bukkit;
import org.bukkit.Material;
import org.bukkit.block.Block;
import org.bukkit.command.CommandSender;
import org.bukkit.entity.Player;
import org.bukkit.event.EventHandler;
import org.bukkit.event.EventPriority;
import org.bukkit.event.block.Action;
import org.bukkit.event.block.BlockBreakEvent;
import org.bukkit.event.player.PlayerInteractEvent;
import com.nisovin.magicspells.MagicSpells;
import com.nisovin.magicspells.Spell;
import com.nisovin.magicspells.Spellbook;
import com.nisovin.magicspells.events.SpellLearnEvent;
import com.nisovin.magicspells.events.SpellLearnEvent.LearnSource;
import com.nisovin.magicspells.materials.MagicMaterial;
import com.nisovin.magicspells.spelleffects.EffectPosition;
import com.nisovin.magicspells.spells.CommandSpell;
import com.nisovin.magicspells.util.MagicConfig;
import com.nisovin.magicspells.util.MagicLocation;
public class SpellbookSpell extends CommandSpell {
private int defaultUses;
private boolean destroyBookcase;
private MagicMaterial spellbookBlock;
private String strUsage;
private String strNoSpell;
private String strCantTeach;
private String strNoTarget;
private String strHasSpellbook;
private String strCantDestroy;
private String strLearnError;
private String strCantLearn;
private String strAlreadyKnown;
private String strLearned;
private ArrayList<MagicLocation> bookLocations;
private ArrayList<String> bookSpells;
private ArrayList<Integer> bookUses;
public SpellbookSpell(MagicConfig config, String spellName) {
super(config,spellName);
defaultUses = getConfigInt("default-uses", -1);
destroyBookcase = getConfigBoolean("destroy-when-used-up", false);
spellbookBlock = MagicSpells.getItemNameResolver().resolveBlock(getConfigString("spellbook-block", "bookshelf"));
strUsage = getConfigString("str-usage", "Usage: /cast spellbook <spell> [uses]");
strNoSpell = getConfigString("str-no-spell", "You do not know a spell by that name.");
strCantTeach = getConfigString("str-cant-teach", "You can't create a spellbook with that spell.");
strNoTarget = getConfigString("str-no-target", "You must target a bookcase to create a spellbook.");
strHasSpellbook = getConfigString("str-has-spellbook", "That bookcase already has a spellbook.");
strCantDestroy = getConfigString("str-cant-destroy", "You cannot destroy a bookcase with a spellbook.");
strLearnError = getConfigString("str-learn-error", "");
strCantLearn = getConfigString("str-cant-learn", "You cannot learn the spell in this spellbook.");
strAlreadyKnown = getConfigString("str-already-known", "You already know the %s spell.");
strLearned = getConfigString("str-learned", "You have learned the %s spell!");
bookLocations = new ArrayList<MagicLocation>();
bookSpells = new ArrayList<String>();
bookUses = new ArrayList<Integer>();
loadSpellbooks();
}
@Override
public PostCastAction castSpell(Player player, SpellCastState state, float power, String[] args) {
if (state == SpellCastState.NORMAL) {
if (args == null || args.length < 1 || args.length > 2 || (args.length == 2 && !args[1].matches("^[0-9]+$"))) {
// fail: show usage string
sendMessage(player, strUsage);
} else {
// check for reload
if (player.isOp() && args[0].equalsIgnoreCase("reload")) {
bookLocations = new ArrayList<MagicLocation>();
bookSpells = new ArrayList<String>();
bookUses = new ArrayList<Integer>();
loadSpellbooks();
player.sendMessage("Spellbook file reloaded.");
return PostCastAction.ALREADY_HANDLED;
}
Spellbook spellbook = MagicSpells.getSpellbook(player);
Spell spell = MagicSpells.getSpellByInGameName(args[0]);
if (spellbook == null || spell == null || !spellbook.hasSpell(spell)) {
// fail: no such spell
sendMessage(player, strNoSpell);
} else if (!MagicSpells.getSpellbook(player).canTeach(spell)) {
// fail: can't teach
sendMessage(player, strCantTeach);
} else {
Block target = getTargetedBlock(player, 10);
if (target == null || !spellbookBlock.equals(target)) {
// fail: must target a bookcase
sendMessage(player, strNoTarget);
} else if (bookLocations.contains(target.getLocation())) {
// fail: already a spellbook there
sendMessage(player, strHasSpellbook);
} else {
// create spellbook
bookLocations.add(new MagicLocation(target.getLocation()));
bookSpells.add(spell.getInternalName());
if (args.length == 1) {
bookUses.add(defaultUses);
} else {
bookUses.add(Integer.parseInt(args[1]));
}
saveSpellbooks();
sendMessage(player, formatMessage(strCastSelf, "%s", spell.getName()));
playSpellEffects(player, target.getLocation());
return PostCastAction.NO_MESSAGES;
}
}
}
return PostCastAction.ALREADY_HANDLED;
}
return PostCastAction.HANDLE_NORMALLY;
}
private void removeSpellbook(int index) {
bookLocations.remove(index);
bookSpells.remove(index);
bookUses.remove(index);
saveSpellbooks();
}
@EventHandler(priority=EventPriority.HIGH)
public void onPlayerInteract(PlayerInteractEvent event) {
if (event.isCancelled()) return;
if (event.hasBlock() && spellbookBlock.equals(event.getClickedBlock()) && event.getAction() == Action.RIGHT_CLICK_BLOCK) {
MagicLocation loc = new MagicLocation(event.getClickedBlock().getLocation());
if (bookLocations.contains(loc)) {
event.setCancelled(true);
Player player = event.getPlayer();
int i = bookLocations.indexOf(loc);
Spellbook spellbook = MagicSpells.getSpellbook(player);
Spell spell = MagicSpells.getSpellByInternalName(bookSpells.get(i));
if (spellbook == null || spell == null) {
// fail: something's wrong
sendMessage(player, strLearnError);
} else if (!spellbook.canLearn(spell)) {
// fail: can't learn
sendMessage(player, formatMessage(strCantLearn, "%s", spell.getName()));
} else if (spellbook.hasSpell(spell)) {
// fail: already known
sendMessage(player, formatMessage(strAlreadyKnown, "%s", spell.getName()));
} else {
// call learn event
SpellLearnEvent learnEvent = new SpellLearnEvent(spell, player, LearnSource.SPELLBOOK, event.getClickedBlock());
Bukkit.getPluginManager().callEvent(learnEvent);
if (learnEvent.isCancelled()) {
// fail: plugin cancelled it
sendMessage(player, formatMessage(strCantLearn, "%s", spell.getName()));
} else {
// teach the spell
spellbook.addSpell(spell);
spellbook.save();
sendMessage(player, formatMessage(strLearned, "%s", spell.getName()));
playSpellEffects(EffectPosition.DELAYED, player);
int uses = bookUses.get(i);
if (uses > 0) {
uses--;
if (uses == 0) {
// remove the spellbook
if (destroyBookcase) {
bookLocations.get(i).getLocation().getBlock().setType(Material.AIR);
}
removeSpellbook(i);
} else {
bookUses.set(i, uses);
}
}
}
}
}
}
}
@EventHandler
public void onBlockBreak(BlockBreakEvent event) {
if (event.isCancelled()) return;
if (spellbookBlock.equals(event.getBlock())) {
MagicLocation loc = new MagicLocation(event.getBlock().getLocation());
if (bookLocations.contains(loc)) {
if (event.getPlayer().isOp() || event.getPlayer().hasPermission("magicspells.advanced.spellbook")) {
// remove the bookcase
int i = bookLocations.indexOf(loc);
removeSpellbook(i);
} else {
// cancel it
event.setCancelled(true);
sendMessage(event.getPlayer(), strCantDestroy);
}
}
}
}
@Override
public boolean castFromConsole(CommandSender sender, String[] args) {
if (sender.isOp() && args != null && args.length > 0 && args[0].equalsIgnoreCase("reload")) {
bookLocations = new ArrayList<MagicLocation>();
bookSpells = new ArrayList<String>();
bookUses = new ArrayList<Integer>();
loadSpellbooks();
sender.sendMessage("Spellbook file reloaded.");
return true;
} else {
return false;
}
}
@Override
public List<String> tabComplete(CommandSender sender, String partial) {
if (sender instanceof Player && !partial.contains(" ")) {
return tabCompleteSpellName(sender, partial);
}
return null;
}
private void loadSpellbooks() {
try {
Scanner scanner = new Scanner(new File(MagicSpells.plugin.getDataFolder(), "books.txt"));
while (scanner.hasNext()) {
String line = scanner.nextLine();
if (!line.equals("")) {
try {
String[] data = line.split(":");
MagicLocation loc = new MagicLocation(data[0], Integer.parseInt(data[1]), Integer.parseInt(data[2]), Integer.parseInt(data[3]));
int uses = Integer.parseInt(data[5]);
bookLocations.add(loc);
bookSpells.add(data[4]);
bookUses.add(uses);
} catch (Exception e) {
MagicSpells.plugin.getServer().getLogger().severe("MagicSpells: Failed to load spellbook: " + line);
}
}
}
} catch (FileNotFoundException e) {
}
}
private void saveSpellbooks() {
try {
BufferedWriter writer = new BufferedWriter(new FileWriter(new File(MagicSpells.plugin.getDataFolder(), "books.txt"), false));
MagicLocation loc;
for (int i = 0; i < bookLocations.size(); i++) {
loc = bookLocations.get(i);
writer.write(loc.getWorld() + ":" + (int)loc.getX() + ":" + (int)loc.getY() + ":" + (int)loc.getZ() + ":");
writer.write(bookSpells.get(i) + ":" + bookUses.get(i));
writer.newLine();
}
writer.close();
} catch (Exception e) {
MagicSpells.plugin.getServer().getLogger().severe("MagicSpells: Error saving spellbooks");
}
}
}